@samva/vite 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -1,87 +1,96 @@
1
1
  # @samva/vite agent guide
2
2
 
3
3
  `@samva/vite` is the local authoring toolchain for Samva templates written with `@samva/markup`: a
4
- Vite plugin that serves the visual editor, and programmatic entry points that build, render, and
5
- export a template project. It compiles nothing itself; every render goes through
6
- `@samva/markup/compiler`'s `compileEmailProject`, the same compile step a hosted Samva build runs, so
7
- a local render does not drift from a published one. Publishing and hosted synchronization are not
4
+ Vite plugin that serves the visual editor, a config-free dev server, and programmatic entry points
5
+ that build, render, and export a template project. A template is one TSX file in a static profile,
6
+ and nothing here runs it: every check, render and preview is `compileTemplate` from
7
+ `@samva/markup/static` followed by `renderIr`, the same two steps a hosted Samva build takes, so a
8
+ local render does not drift from a published one. Publishing and hosted synchronization are not
8
9
  here: they are ordinary Git plus `samva templates publish` from `@samva/cli` (see
9
10
  `samva templates --help`).
10
11
 
11
12
  ## Entry points
12
13
 
13
- - `@samva/vite` — `samvaEditor()`, the plugin you add to `vite.config.ts`. Options are typed by
14
- `SamvaEditorPluginOptions`; the README lists them.
15
- - `@samva/vite/build` — `buildTemplates()` and `createTemplateBuildSession()` for programmatic
16
- checks and builds. The `samva` CLI loads this module from your project, not from its own bundle.
17
- - `@samva/vite/project` — the folder-of-templates surface: `loadEmailCatalog()`,
18
- `renderEmailFixtures()`, `createEmailProjectSession()`, `exportEmailProject()`.
19
- - `@samva/vite/email` — `createEmailRenderer()` and `renderEmailProject()` for rendering one email
20
- project from source.
21
- - `@samva/vite/emit` — module-preview integration for structured SMS and WhatsApp entries.
14
+ - `@samva/vite` — `samvaEditor()`, the plugin you add to `vite.config.ts` when the defaults do not
15
+ cover your setup. Options are typed by `SamvaEditorPluginOptions`; the README lists them.
16
+ - `@samva/vite/dev` — `startDevServer({ root, dir?, port?, host?, open? })`: a Vite server with no
17
+ config file, `samvaEditor()`, and defaults. It is what `samva templates dev` starts.
18
+ - `@samva/vite/build` — `buildTemplates(options)` and `createTemplateBuildSession(options)` for
19
+ programmatic checks and builds. The `samva` CLI loads this module from your project, not from its
20
+ own bundle.
21
+ - `@samva/vite/render` — `renderTemplate()` and `renderChannel()`: one template and fixture to a
22
+ plain JSON result.
23
+ - `@samva/vite/project` — the folder-of-templates surface: `loadProject()`, `compileProject()`,
24
+ `createProjectSession()`, `exportProject()` and the render helpers over a compiled entry.
22
25
 
23
26
  Install `@samva/markup`, `@samva/vite`, and the `vite` peer as dev dependencies of the template
24
- project and keep them in its lockfile. The CLI's `templates check`, `templates build`, and the
25
- editor all resolve these packages from the project, so the lockfile is what pins the compiler
26
- version your previews, checks, and builds use.
27
+ project and keep them in its lockfile. The CLI's `templates check`, `templates build`,
28
+ `templates render` and the editor all resolve these packages from the project, so the lockfile is
29
+ what pins the compiler version your previews, checks, and builds use.
27
30
 
28
31
  ## How you hurt yourself here
29
32
 
30
- - **Expecting your Vite config to apply to email rendering.** Every email render, in the editor,
31
- the CLI, and the programmatic entry points, runs in an evaluator this package roots at its own
32
- directory with no config file, so your `vite.config.ts` aliases and plugins never reach an email
33
- template. Bare imports resolve from the package root, which is what makes `@samva/markup` resolve
34
- under both hoisted and per-package installs. Import shared helpers by relative path; an alias that
35
- works elsewhere in your app fails here.
36
- - **Side effects at module scope.** Discovery imports every `.tsx` under the templates directory
37
- and asks whether its default export is a template definition, and a rebuild re-reads the whole
38
- project. Top-level code in every module, helpers included, runs whenever the project is
39
- evaluated; keep it free of I/O and mutable global state.
33
+ - **Expecting a template module to run.** The compiler reads the file and never executes it, so
34
+ top-level code, imports of constants, and helper calls do nothing. A value a template needs is
35
+ input data, a fixture literal, or an import the static profile allows (a partial, a stylesheet,
36
+ an asset). `samva templates check` reports what the profile refuses, with a location and a fix.
37
+ - **Expecting your Vite config to apply to templates.** Nothing about a template goes through
38
+ Vite's module graph, so your `vite.config.ts` aliases and plugins never reach one. Import
39
+ partials by relative path.
40
+ - **Putting templates where the project root cannot see them.** Files are addressed relative to
41
+ the project root, which holds `theme.css`, shared imports and `.samva/`. A templates directory
42
+ outside it is refused rather than half-read. Set `projectRoot` (plugin) or `root` (entry points)
43
+ to a folder that contains both.
44
+ - **Scanning the wrong directories.** With no `dir`, `templates/` and `emails/` are both scanned,
45
+ whichever exist. Setting `dir` scans only that one, so a project that keeps templates in both
46
+ and sets `dir` loses the other.
40
47
  - **Relying on a file path as template identity.** Identity is the `id` the definition declares.
41
48
  Two definitions declaring one id is a reported error, and moving a file does not change its id.
42
- Helper modules need no marker or exclusion list: a module is a template only if its default export
43
- is a definition.
44
- - **Exporting for delivery with the default asset base.** `exportEmailProject` renders with relative
49
+ Helper modules need no marker or exclusion list: a module is a template only if its default
50
+ export is `defineTemplate`. An editor document is a template's path plus its channel.
51
+ - **A broken file elsewhere in the project.** The compiler reads an entry and the files it imports.
52
+ A file it cannot parse is a finding that names the file, and it stops the templates that import
53
+ it; the others keep their render.
54
+ - **Exporting for delivery with the default asset base.** `exportProject` renders with relative
45
55
  URLs and writes each page at `templates/<id>/<fixture>.html` beside a content-addressed `assets/`
46
56
  directory, so its default base, `DEFAULT_EXPORT_ASSET_BASE`, only works when those files are
47
- browsed from disk. An inbox has no directory to resolve a relative URL against, so delivery refuses
48
- one. For an export you will send, pass the https origin that will serve the assets.
49
- - **Reusing a renderer without a stable namespace.** `createEmailRenderer` keeps one evaluator
50
- across renders, as a dev server does, and gives each project its own module-id namespace. Omit
51
- `namespace` and every `load` takes a fresh one, which isolates unrelated projects but grows the
52
- graph on every reload. A watch loop over one project passes the same `namespace` each time so its
53
- ids stay stable. Each `load` re-evaluates its own namespace's modules; `invalidate()` also drops
54
- the modules namespaces share. `close()` releases the evaluator.
55
- - **Expecting the email editor to overwrite concurrent edits.** The editor writes back to the
56
- entry's canonical `.tsx` source after checking that the file on disk is still the revision it
57
- read, so a change you saved elsewhere is not overwritten; reload the entry and retry. SMS and
58
- WhatsApp entries are read-only in the editor.
57
+ browsed from disk. An inbox has no directory to resolve a relative URL against, so delivery
58
+ refuses one. For an export you will send, pass the https origin that will serve the assets.
59
+ - **Expecting the editor to overwrite concurrent edits.** The editor writes back to the entry's
60
+ `.tsx` source after checking that the file on disk is still the revision it read, so a change you
61
+ saved elsewhere is not overwritten; reload the entry and retry.
62
+ - **Committing or hand-editing `.samva/`.** `.samva/` holds files this package generates. It is
63
+ gitignored, like `.next/` and `.tanstack/`, and changes inside it never recompile templates.
64
+ `samva:brand` resolves live first, refreshes `.samva/brands/<slug>.json` from every live answer,
65
+ and falls back to that copy, warning when it is used or was older than the live brand. The API
66
+ owns the brand; a project with neither credentials nor a cached copy fails with
67
+ `brand-unavailable`.
59
68
 
60
69
  ## Rules
61
70
 
62
- - **Template entries are `.tsx`.** Discovery ignores every other extension.
71
+ - **Template entries are `.tsx`.** Discovery ignores every other extension, hidden folders and
72
+ `node_modules`.
63
73
  - **Template ids and fixture keys match `TEMPLATE_NAME_PATTERN`** from `@samva/markup/template`,
64
74
  because both are written into export paths. Code that builds a path from them imports that
65
75
  pattern instead of restating it.
66
76
  - **Fixtures are validated against the template's own schema before they render.** A bad fixture is
67
77
  a finding on that template, not a runtime surprise at send time.
68
78
  - **One failure stays with its template.** A refused render reports against its fixture, and an
69
- email entry that does not build stays in the catalog with a null render and the findings that
70
- stopped it. SMS and WhatsApp entries that stop compiling keep their last successful preview,
71
- labeled stale, until the file compiles again or is deleted.
72
- - **Results are values.** `@samva/vite/build` and the preview surfaces return discriminated results
73
- with an `ok` field, and `@samva/vite/emit` passes through the compiler's better-result `Result`. A
74
- missing `vite` peer, or an entry whose default export is not a template definition where one is
75
- required, throws.
79
+ entry that does not compile stays in the catalog with a null render and the findings that
80
+ stopped it; an editor document whose file stops compiling keeps its place the same way.
81
+ - **Results are values.** `buildTemplates`, `renderTemplate` and `renderChannel` return
82
+ discriminated results with an `ok` field and never throw for a finding about your source. A
83
+ missing `vite` peer, or a templates directory outside the project root, throws.
76
84
  - **The asset base is a build input, not authored source.** The same content can be built for one
77
85
  origin and exported for another. The dev server is not an https origin, so editor previews
78
86
  reference assets through the editor's own route by path.
79
87
 
80
88
  ## Proving your setup
81
89
 
82
- - `samva templates check --dir <templates-dir>` executes every declared fixture and reports
90
+ - `samva templates check` compiles every template, renders every declared fixture, and reports
83
91
  findings; `--watch` keeps it running while you edit.
84
- - `vite` with `samvaEditor()` configured serves the editor at the configured `route`; every entry
85
- should appear with its fixtures.
86
- - `exportEmailProject({ dir, out })` writes browsable HTML and text for every fixture and a
87
- `catalog.json` with the asset mapping and findings, without Samva credentials.
92
+ - `samva templates dev`, or `vite` with `samvaEditor()` configured, serves the editor at the
93
+ configured `route`; every template should appear with its fixtures, one document per channel.
94
+ - `exportProject({ root, out })` writes browsable HTML and text for every fixture and a
95
+ `catalog.json` with the asset mapping and findings, without Samva credentials unless the project
96
+ imports `samva:brand` and has no cached copy.
package/README.md CHANGED
@@ -1,31 +1,39 @@
1
1
  # @samva/vite
2
2
 
3
- The unified local editor for Samva email, SMS, and WhatsApp templates. One Vite
4
- plugin discovers `.tsx` files, evaluates them through the Samva JSX runtime,
5
- applies the project theme, and serves the full visual editor with HMR.
3
+ The local editor, dev server and build for Samva templates. A template is one TSX file that
4
+ `@samva/markup` compiles without running it; this package serves the visual editor over the
5
+ compiled templates, and checks, builds, renders and exports them, so what you preview is what a
6
+ publication renders.
6
7
 
7
- ## Install
8
+ ## Start
8
9
 
9
- Keep the authoring library and editor in the project, with Vite provided as the editor's peer:
10
+ `samva templates dev` bundles the dev server with sensible defaults, so most projects configure
11
+ nothing and need no `vite.config.ts`:
10
12
 
11
13
  ```sh
12
- npm install --save-dev @samva/markup @samva/vite vite
14
+ samva templates dev
13
15
  ```
14
16
 
15
- `samva templates init` creates a project from the canonical starter, which contains these
16
- dependencies and an equivalent `vite.config.ts`. Existing project scripts remain untouched.
17
+ `samva templates init` creates a project from the canonical starter. Templates live in `templates/`,
18
+ and `emails/` is also recognized. `@samva/vite` stays a public, configurable plugin for setups
19
+ those defaults cannot cover: templates in another folder, a monorepo, or a Vite server that also
20
+ runs your other plugins.
21
+
22
+ ```sh
23
+ npm install --save-dev @samva/markup @samva/vite vite
24
+ ```
17
25
 
18
26
  ```ts
19
27
  import { samvaEditor } from "@samva/vite";
20
28
  import { defineConfig } from "vite";
21
29
 
22
30
  export default defineConfig({
23
- plugins: [samvaEditor({ templatesDir: "emails" })],
31
+ plugins: [samvaEditor()],
24
32
  });
25
33
  ```
26
34
 
27
- Run `vite` and open `http://localhost:5173/`. The default assumes this dev server
28
- exists only for Samva authoring:
35
+ Run `vite` and open `http://localhost:5173/`. The default assumes this dev server exists only for
36
+ Samva authoring:
29
37
 
30
38
  - editor UI: `/`
31
39
  - editor API and event streams: `/api/*`
@@ -34,91 +42,153 @@ exists only for Samva authoring:
34
42
  To share a Vite server with another app, choose a route:
35
43
 
36
44
  ```ts
37
- samvaEditor({ templatesDir: "emails", route: "/__samva" });
45
+ samvaEditor({ route: "/__samva" });
38
46
  ```
39
47
 
40
- That mounts the UI at `/__samva/`, the API at `/__samva/api/*`, and the bundled
41
- assets at `/__samva/assets/*`. Requests outside the configured route pass through
42
- to the rest of the Vite middleware stack.
48
+ That mounts the UI at `/__samva/`, the API at `/__samva/api/*`, and the bundled assets at
49
+ `/__samva/assets/*`. Requests outside the configured route pass through to the rest of the Vite
50
+ middleware stack.
51
+
52
+ ## Options
53
+
54
+ | Option | Type | Default | Description |
55
+ | -------------- | ----------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------- |
56
+ | `templatesDir` | `string` | `templates/` and `emails/` | The one directory to scan for `.tsx` templates, relative to the project root. Unset scans whichever exist. |
57
+ | `projectRoot` | `string` | the Vite root | The folder holding `theme.css`, shared imports and `.samva/`, relative to the Vite root. Contains `templatesDir`. |
58
+ | `route` | `string` | `"/"` | Absolute editor UI route; API and assets are mounted beneath it. |
59
+ | `theme` | `string \| false` | `"theme.css"` | Project theme file relative to the project root, or `false` to compile without one. |
60
+
61
+ For templates in another folder of a monorepo, set `projectRoot` to a folder that contains both the
62
+ templates and the files they import; a templates directory outside the project root is an error.
43
63
 
44
64
  ## Authoring files
45
65
 
46
- Email `.tsx` entries default-export `defineEmail({ id, schema, fixtures, render })` from
47
- `@samva/markup/template`. Use the `@samva/markup/email` JSX runtime and primitives from
48
- `@samva/markup/email/components`. Ordinary synchronous helpers, conditions and loops execute
49
- against actual validated JSON. Shared helper modules are excluded from the template catalog;
50
- ids are stable independently of paths and must be unique within the project.
66
+ A template default-exports `defineTemplate` from `@samva/markup` and is written in the static
67
+ profile: ordinary JSX, HTML and Tailwind that the compiler reads and never executes. The editor
68
+ previews every declared fixture, offers source-aware edits where the source intent is unambiguous,
69
+ and saves them to the file.
51
70
 
52
71
  ```tsx
53
- /** @jsxImportSource @samva/markup/email */
54
- import { Schema } from "effect";
72
+ import { defineTemplate } from "@samva/markup";
55
73
  import { Email, Section } from "@samva/markup/email/components";
56
- import { defineEmail } from "@samva/markup/template";
74
+ import { jsonSchema } from "@samva/markup/input-schema";
57
75
 
58
- export default defineEmail({
76
+ export default defineTemplate({
59
77
  id: "welcome",
60
- schema: Schema.toStandardJSONSchemaV1(
61
- Schema.Struct({ firstName: Schema.String, items: Schema.Array(Schema.String) }),
62
- ),
63
- fixtures: { basic: { firstName: "Ada", items: ["Your first project"] } },
64
- render: (input) => ({
65
- subject: `Welcome, ${input.firstName}`,
66
- preheader: "Your account is ready",
67
- body: (
78
+ schema: jsonSchema<{ firstName: string }>({
79
+ type: "object",
80
+ required: ["firstName"],
81
+ properties: { firstName: { type: "string" } },
82
+ additionalProperties: false,
83
+ }),
84
+ fixtures: { basic: { firstName: "Ada" } },
85
+ email: {
86
+ subject: (input) => `Welcome, ${input.firstName}`,
87
+ preheader: () => "Your account is ready",
88
+ body: (input) => (
68
89
  <Email>
69
90
  <Section>
70
91
  <h1>Welcome, {input.firstName}</h1>
71
- {input.items.length > 0 && (
72
- <ul>
73
- {input.items.map((item) => (
74
- <li key={item}>{item}</li>
75
- ))}
76
- </ul>
77
- )}
78
92
  </Section>
79
93
  </Email>
80
94
  ),
81
- }),
95
+ },
82
96
  });
83
97
  ```
84
98
 
85
- The email editor previews declared fixtures and offers source-aware edits where the source intent
86
- is unambiguous. Source is canonical. Failed email entries remain visible with diagnostics and a
87
- null render. SMS and WhatsApp remain read-only structured previews; an invalid edit retains their
88
- last successful emission and labels it stale. Deleting an entry removes its preview.
99
+ Any other `.tsx` module in the templates directory (a partial, a component) is an ordinary module
100
+ that templates import; a module is a template only if its default export is `defineTemplate`.
101
+ Identity is the `id` the definition declares, unique within the project. A template that declares
102
+ `sms` or `whatsapp` beside `email` shows one editor document per channel over the same file.
89
103
 
90
- The browser canvas is a structural preview of Samva's modern and Outlook-safe
91
- compiler output. It does not emulate the exact Gmail, Outlook, Apple Mail, or
92
- Yahoo rendering engines. Use compatibility findings and inbox tests for
93
- client-specific proof.
104
+ The browser canvas is a structural preview of Samva's modern and Outlook-safe compiler output. It
105
+ does not emulate the exact Gmail, Outlook, Apple Mail, or Yahoo rendering engines. Use
106
+ compatibility findings and inbox tests for client-specific proof.
94
107
 
95
- ## Options
108
+ ## Theme, check, and publish
96
109
 
97
- | Option | Type | Default | Description |
98
- | -------------- | ----------------- | ------------- | ---------------------------------------------------------------- |
99
- | `templatesDir` | `string` | `"emails"` | Recursive `.tsx` template directory. |
100
- | `route` | `string` | `"/"` | Absolute editor UI route; API and assets are mounted beneath it. |
101
- | `theme` | `string \| false` | `"theme.css"` | Project theme file, or `false` to disable discovery. |
110
+ A root `theme.css` uses Tailwind v4 `@theme` syntax and may `@import` other project `.css` files
111
+ and `samva:brand`; any other import is a compile error. A change to the theme, a partial, or an
112
+ imported stylesheet recompiles every template that reads it.
102
113
 
103
- ## Theme, check, and publish
114
+ `samva templates check` and `buildTemplates()` compile every template, render every declared
115
+ fixture through each channel the template declares, and check the email output against the client
116
+ matrix. `buildTemplates()` returns `files`, one per template entry, with `ok`, the template's IR,
117
+ input schema, each fixture's rendered output, and structured diagnostics; helper modules have no
118
+ entry. A template requires at least one fixture.
119
+
120
+ ```ts
121
+ import { buildTemplates } from "@samva/vite/build";
122
+
123
+ const result = await buildTemplates({ root: ".", assetBase: "https://assets.example.com/p" });
124
+ ```
104
125
 
105
- A root `theme.css` uses Tailwind v4 `@theme` syntax. Theme changes recompile
106
- local previews while preserving the last valid theme when an edit is temporarily
107
- invalid.
126
+ `buildTemplates(options)` and `createTemplateBuildSession(options)` take `root`, `dir`, `theme`,
127
+ `assetBase` and `offline`. Imported email assets require an HTTPS `assetBase`, which names where
128
+ their bytes are served in delivered messages. `createTemplateBuildSession` also reports file
129
+ changes for `check --watch`.
108
130
 
109
- `samva templates check` and `buildTemplates()` execute every email fixture and validate its
110
- actual input. `buildTemplates()` returns `files`, each with a `preview.kind` discriminator:
111
- `email` contains input schema, compiled module graph, actual fixture subject/HTML/text and
112
- structured diagnostics; `structured` contains the SMS/WhatsApp preview. Helper modules have no
113
- file entry. An email requires at least one fixture.
131
+ `samva templates build --out dist/templates.json` writes the CLI's local artifact. Check writes no
132
+ files. A successful fixture check does not prove every possible input branch.
114
133
 
115
- `samva templates build --dir emails --out dist/templates.json` writes the CLI's version 2 local
116
- artifact. Check writes no files. `check --watch` observes changes; `--jsonl` emits one observation
117
- per rebuild and `--json` is one-shot. A successful fixture check does not prove every possible
118
- input branch.
134
+ ## Render one fixture
119
135
 
120
- Imported email assets require `assetBase` in `buildTemplates()` or `--asset-base` in CLI checks
121
- and builds, naming their actual HTTPS serving base. No credentials are needed for local work.
136
+ `samva templates render` runs this package's `render` entry from your project, so it uses the
137
+ compiler version your lockfile pins. The results are plain JSON.
138
+
139
+ ```ts
140
+ import { renderChannel, renderTemplate } from "@samva/vite/render";
141
+
142
+ const email = await renderTemplate({ root: ".", template: "welcome", fixture: "basic" });
143
+ // { ok: true, subject, preheader?, html, text, diagnostics } or { ok: false, diagnostics }
144
+
145
+ const sms = await renderChannel({ root: ".", template: "order", fixture: "first", channel: "sms" });
146
+ ```
147
+
148
+ `template` is a template id or the project-relative path of its entry. `locale` and `timeZone`
149
+ override the formatters' defaults.
150
+
151
+ ## Dev server without a config file
152
+
153
+ ```ts
154
+ import { startDevServer } from "@samva/vite/dev";
155
+
156
+ const server = await startDevServer({ root: ".", port: 5173, open: true });
157
+ console.log(server.url);
158
+ await server.close();
159
+ ```
160
+
161
+ `startDevServer` creates a Vite server with `configFile: false` and `samvaEditor()`, which is what
162
+ `samva templates dev` does.
163
+
164
+ ## The organization brand
165
+
166
+ A project that imports `samva:brand` (the default brand) or `samva:brand/<slug>` compiles against
167
+ its organization's brand. The platform brand record is the source of truth; each publication pins
168
+ the brand version it was built with. The editor, `buildTemplates()`, and `exportProject()` resolve
169
+ it in this order:
170
+
171
+ 1. **Live.** With `SAMVA_API_KEY` in the environment or the project's `.env` (and `SAMVA_API_URL`
172
+ for another API), the brand is fetched directly. Without one, the `samva` CLI on `PATH` is asked
173
+ (`samva brands get --json`) and answers with your `samva login` session, which stays in the
174
+ operating-system keyring the CLI owns. A live answer refreshes the cached copy.
175
+ 2. **Cached copy.** `.samva/brands/<slug>.json`, written by every live fetch and by
176
+ `samva brands pull [slug]`, with a `brand-offline` warning.
177
+ 3. **Neither.** The build fails with `brand-unavailable`, naming both fixes.
178
+
179
+ When the live brand's digest differs from the cached copy's, the build warns with
180
+ `brand-snapshot-stale` and refreshes the copy. Warnings are build diagnostics and editor log lines;
181
+ they never fail a build. The editor and `check --watch` reuse a resolution briefly, so a brand
182
+ edited elsewhere arrives within seconds rather than on the next save. A project that imports no
183
+ brand needs no credentials for local work.
184
+
185
+ ## The `.samva/` folder
186
+
187
+ `.samva/` holds files this package generates in your project, the way `.next/` and `.tanstack/`
188
+ do. Keep it in `.gitignore`. It is not part of the project's source, so changes inside it never
189
+ recompile templates.
190
+
191
+ ## Publish
122
192
 
123
193
  For a hosted project, commit and push the checked TSX tree with ordinary Git, then publish one exact
124
194
  commit:
@@ -134,21 +204,23 @@ The CLI's repository-local credential helper obtains a short-lived Artifact cred
134
204
  Samva's credential protocol when Git needs one. It validates the repository host/path and does not
135
205
  store the token in Git config or the remote URL. `templates publish` requires a clean worktree and
136
206
  an exact commit reachable from the fetched `origin` default branch; it creates an immutable
137
- publication receipt and does not push or rewrite history.
138
-
139
- Hosted builds consume the committed Bun text lockfile version 2 or npm lockfile version 3.
140
- Keep dependencies and the supported lockfile in the project. Sending selects an exact immutable
207
+ publication receipt and does not push or rewrite history. Sending selects an exact immutable
141
208
  publication by default; later source edits do not change that publication.
142
209
 
143
210
  ## Local export
144
211
 
145
212
  ```ts
146
- import { exportEmailProject } from "@samva/vite/project";
213
+ import { exportProject } from "@samva/vite/project";
147
214
 
148
- const receipt = await exportEmailProject({ dir: ".", out: "dist/email" });
215
+ const receipt = await exportProject({ root: ".", out: "dist/email" });
149
216
  ```
150
217
 
151
- Export writes `templates/<id>/<fixture>.html` and `.txt`, content-addressed files under `assets/`,
152
- and `catalog.json` with the asset mapping and findings. The default `../../assets` base resolves
153
- from each exported page. For delivery, supply the actual HTTPS `assetBase` and serve those bytes
154
- there; relative URLs are for local export. External HTTPS images stay external references.
218
+ `root` is the project: its `theme.css` themes the export, as in `buildTemplates()`; `dir` narrows
219
+ the templates directory, `theme` names another file or `false` disables it, and `samva:brand`
220
+ resolves as described above.
221
+
222
+ Export writes `templates/<id>/<fixture>.html` and `.txt`, plus `.sms.txt` and `.whatsapp.json` for
223
+ those channels, content-addressed files under `assets/`, and `catalog.json` with the asset mapping
224
+ and findings. The default `../../assets` base resolves from each exported page. For delivery,
225
+ supply the actual HTTPS `assetBase` and serve those bytes there; relative URLs are for local
226
+ export. External HTTPS images stay external references.
package/dist/build.d.ts CHANGED
@@ -1,61 +1,66 @@
1
- import { ModulePreview } from "./emit.js";
2
- import { CompiledEmailProject } from "@samva/markup/compiler";
1
+ import { _ as TemplateChannel, a as ProjectOptions } from "./project-CiUuzKsC.js";
3
2
  import { EmailDiagnostic } from "@samva/markup/email/diagnostics";
3
+ import { RenderedIrEmail, RenderedIrWhatsApp, TemplateIr } from "@samva/markup/ir";
4
4
  //#region src/build.d.ts
5
- /** One declared input rendered through executable email source. */
6
- interface EmailFixtureBuild {
5
+ /** The compiler a build ran, as a publication records it. */
6
+ declare const COMPILER: string;
7
+ /** One declared input rendered through every channel the template declares. */
8
+ interface FixtureBuild {
7
9
  readonly name: string;
8
10
  readonly input: unknown;
9
11
  readonly ok: boolean;
10
- readonly subject?: string | undefined;
11
- readonly preheader?: string | undefined;
12
- readonly html?: string | undefined;
13
- readonly text?: string | undefined;
12
+ readonly email?: Pick<RenderedIrEmail, "subject" | "preheader" | "html" | "text"> | undefined;
13
+ readonly sms?: string | undefined;
14
+ readonly whatsapp?: RenderedIrWhatsApp | undefined;
14
15
  readonly diagnostics: readonly EmailDiagnostic[];
15
16
  }
16
- /** Executable email artifacts retain their input contract, module graph and actual fixture output. */
17
- interface EmailBuildPreview {
18
- readonly kind: "email";
17
+ /** A template's IR, its input contract, and what each declared fixture rendered. */
18
+ interface TemplateBuild {
19
+ /** The id the definition declares. */
20
+ readonly template: string;
21
+ readonly channels: readonly TemplateChannel[];
19
22
  readonly ok: boolean;
20
- readonly compilerVersion: string;
23
+ /** Project-relative source of the entry. */
24
+ readonly source: string;
25
+ readonly inputSchema: Readonly<Record<string, unknown>>;
26
+ readonly ir: TemplateIr;
27
+ readonly fixtures: readonly FixtureBuild[];
28
+ /** Compile findings, brand findings and asset-base findings. */
21
29
  readonly diagnostics: readonly EmailDiagnostic[];
22
- readonly templates: readonly {
23
- readonly name: string;
24
- readonly channel: "email";
25
- readonly ok: boolean;
26
- readonly source: string;
27
- readonly inputSchema: Readonly<Record<string, unknown>>;
28
- readonly compiled: CompiledEmailProject;
29
- readonly fixtures: readonly EmailFixtureBuild[];
30
- }[];
30
+ /** Target-client findings across the email fixtures. */
31
+ readonly compatibility: readonly EmailDiagnostic[];
31
32
  }
32
- type TemplateBuildPreview = EmailBuildPreview | (ModulePreview & {
33
- readonly kind: "structured";
34
- });
35
- interface BuildTemplatesResult {
36
- /** Authored entry paths, sorted; helper modules have no entry here. */
37
- readonly files: ReadonlyArray<{
38
- readonly id: string;
39
- readonly kind: "tsx";
40
- readonly preview: TemplateBuildPreview;
41
- }>;
33
+ /** One template entry of the project. `template` is absent when the entry did not compile. */
34
+ interface BuildFile {
35
+ /** Project-relative path of the entry. */
36
+ readonly id: string;
37
+ readonly kind: "tsx";
38
+ readonly ok: boolean;
39
+ readonly template?: TemplateBuild | undefined;
40
+ readonly diagnostics: readonly EmailDiagnostic[];
42
41
  }
43
- interface BuildTemplatesOptions {
44
- /** Project root containing shared imports and theme.css; defaults to the parent of dir. */
45
- readonly root?: string | undefined;
46
- readonly theme?: string | false | undefined;
47
- /** Required for imported email assets. Must be their actual HTTPS serving base. */
48
- readonly assetBase?: string | undefined;
42
+ interface BuildTemplatesResult {
43
+ readonly compilerVersion: string;
44
+ /** Template entries, sorted by path; helper modules have no entry here. */
45
+ readonly files: readonly BuildFile[];
46
+ /** Findings about the project as a whole: the brand, a duplicate id. */
47
+ readonly diagnostics: readonly EmailDiagnostic[];
48
+ readonly ok: boolean;
49
49
  }
50
+ interface BuildTemplatesOptions extends ProjectOptions {}
50
51
  interface TemplateBuildSession {
51
52
  readonly build: () => Promise<BuildTemplatesResult>;
52
53
  readonly subscribe: (listener: (path: string) => void) => () => void;
53
54
  readonly close: () => Promise<void>;
54
55
  }
55
- /** Execute each email definition's fixtures; compile structured SMS/WhatsApp through their channel compiler. */
56
- declare const buildTemplates: (dir: string, options?: BuildTemplatesOptions) => Promise<BuildTemplatesResult>;
57
- /** A watcher and module evaluator over the project's installed authoring toolchain. */
58
- declare const createTemplateBuildSession: (dir: string, options?: BuildTemplatesOptions) => Promise<TemplateBuildSession>;
56
+ /**
57
+ * Compile every template in a project and render every fixture it declares
58
+ * through each of its channels, then check the email fixtures against the
59
+ * client matrix. Nothing is evaluated.
60
+ */
61
+ declare const buildTemplates: (options?: BuildTemplatesOptions) => Promise<BuildTemplatesResult>;
62
+ /** A watcher and compiler over a project, for `check --watch`. */
63
+ declare const createTemplateBuildSession: (options?: BuildTemplatesOptions) => TemplateBuildSession;
59
64
  //#endregion
60
- export { BuildTemplatesOptions, BuildTemplatesResult, EmailBuildPreview, EmailFixtureBuild, TemplateBuildPreview, TemplateBuildSession, buildTemplates, createTemplateBuildSession };
65
+ export { BuildFile, BuildTemplatesOptions, BuildTemplatesResult, COMPILER, FixtureBuild, TemplateBuild, TemplateBuildSession, buildTemplates, createTemplateBuildSession };
61
66
  //# sourceMappingURL=build.d.ts.map