@samva/vite 0.8.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 +62 -58
- package/README.md +140 -94
- package/dist/build.d.ts +47 -42
- package/dist/build.js +88 -213
- package/dist/dev.d.ts +22 -0
- package/dist/dev.js +43 -0
- package/dist/editor/assets/index-BtfwuWcY.js +32 -0
- package/dist/editor/assets/index-DIuS_EZu.css +2 -0
- package/dist/editor/index.html +2 -2
- package/dist/editor-Cow7zZ_9.js +780 -0
- package/dist/index.d.ts +11 -4
- package/dist/index.js +1 -1054
- package/dist/project-CiUuzKsC.d.ts +271 -0
- package/dist/project-Cp8qb3s0.js +774 -0
- package/dist/project.d.ts +2 -175
- package/dist/project.js +2 -2
- package/dist/render.d.ts +54 -0
- package/dist/render.js +98 -0
- package/package.json +11 -12
- package/dist/discovery-D2eR-rzi.js +0 -35
- package/dist/editor/assets/index-6Qlbkdhz.js +0 -36
- package/dist/editor/assets/index-Ed_V5wEW.css +0 -2
- package/dist/email.d.ts +0 -60
- package/dist/email.js +0 -126
- package/dist/emit.d.ts +0 -99
- package/dist/emit.js +0 -142
- package/dist/project-RON2Ei7d.js +0 -603
package/AGENTS.md
CHANGED
|
@@ -1,92 +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
|
|
5
|
-
export a template project.
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
14
|
-
`SamvaEditorPluginOptions`; the README lists them.
|
|
15
|
-
- `@samva/vite/
|
|
16
|
-
|
|
17
|
-
- `@samva/vite/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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`,
|
|
25
|
-
editor all resolve these packages from the project, so the lockfile is
|
|
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
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
project
|
|
39
|
-
|
|
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
|
|
43
|
-
is a
|
|
44
|
-
- **
|
|
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
|
|
48
|
-
one. For an export you will send, pass the https origin that will serve the assets.
|
|
49
|
-
- **
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
WhatsApp entries are read-only in the editor.
|
|
59
|
-
- **Committing or hand-editing `.samva/brands`.** `samva:brand` resolves live first and falls
|
|
60
|
-
back to the snapshot `samva brands pull` writes there, warning when it is older than the live
|
|
61
|
-
brand. The API owns the brand, so the directory is gitignored and refreshed with `pull`; a
|
|
62
|
-
project with neither credentials nor a snapshot fails with `brand-unavailable`.
|
|
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`.
|
|
63
68
|
|
|
64
69
|
## Rules
|
|
65
70
|
|
|
66
|
-
- **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`.
|
|
67
73
|
- **Template ids and fixture keys match `TEMPLATE_NAME_PATTERN`** from `@samva/markup/template`,
|
|
68
74
|
because both are written into export paths. Code that builds a path from them imports that
|
|
69
75
|
pattern instead of restating it.
|
|
70
76
|
- **Fixtures are validated against the template's own schema before they render.** A bad fixture is
|
|
71
77
|
a finding on that template, not a runtime surprise at send time.
|
|
72
78
|
- **One failure stays with its template.** A refused render reports against its fixture, and an
|
|
73
|
-
|
|
74
|
-
stopped it
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
missing `vite` peer, or an entry whose default export is not a template definition where one is
|
|
79
|
-
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.
|
|
80
84
|
- **The asset base is a build input, not authored source.** The same content can be built for one
|
|
81
85
|
origin and exported for another. The dev server is not an https origin, so editor previews
|
|
82
86
|
reference assets through the editor's own route by path.
|
|
83
87
|
|
|
84
88
|
## Proving your setup
|
|
85
89
|
|
|
86
|
-
- `samva templates check
|
|
90
|
+
- `samva templates check` compiles every template, renders every declared fixture, and reports
|
|
87
91
|
findings; `--watch` keeps it running while you edit.
|
|
88
|
-
- `vite` with `samvaEditor()` configured serves the editor at the
|
|
89
|
-
should appear with its fixtures.
|
|
90
|
-
- `
|
|
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
|
|
91
95
|
`catalog.json` with the asset mapping and findings, without Samva credentials unless the project
|
|
92
|
-
imports `samva:brand` and has no
|
|
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
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
##
|
|
8
|
+
## Start
|
|
8
9
|
|
|
9
|
-
|
|
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
|
-
|
|
14
|
+
samva templates dev
|
|
13
15
|
```
|
|
14
16
|
|
|
15
|
-
`samva templates init` creates a project from the canonical starter
|
|
16
|
-
|
|
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(
|
|
31
|
+
plugins: [samvaEditor()],
|
|
24
32
|
});
|
|
25
33
|
```
|
|
26
34
|
|
|
27
|
-
Run `vite` and open `http://localhost:5173/`. The default assumes this dev server
|
|
28
|
-
|
|
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,113 +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({
|
|
45
|
+
samvaEditor({ route: "/__samva" });
|
|
38
46
|
```
|
|
39
47
|
|
|
40
|
-
That mounts the UI at `/__samva/`, the API at `/__samva/api/*`, and the bundled
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
54
|
-
import { Schema } from "effect";
|
|
72
|
+
import { defineTemplate } from "@samva/markup";
|
|
55
73
|
import { Email, Section } from "@samva/markup/email/components";
|
|
56
|
-
import {
|
|
74
|
+
import { jsonSchema } from "@samva/markup/input-schema";
|
|
57
75
|
|
|
58
|
-
export default
|
|
76
|
+
export default defineTemplate({
|
|
59
77
|
id: "welcome",
|
|
60
|
-
schema:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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.
|
|
94
|
-
|
|
95
|
-
## Options
|
|
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.
|
|
96
103
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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. |
|
|
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.
|
|
102
107
|
|
|
103
108
|
## Theme, check, and publish
|
|
104
109
|
|
|
105
110
|
A root `theme.css` uses Tailwind v4 `@theme` syntax and may `@import` other project `.css` files
|
|
106
|
-
and `samva:brand`; any other import is a compile error.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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.
|
|
113
|
+
|
|
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
|
+
```
|
|
125
|
+
|
|
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`.
|
|
130
|
+
|
|
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.
|
|
133
|
+
|
|
134
|
+
## Render one fixture
|
|
135
|
+
|
|
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.
|
|
110
150
|
|
|
111
|
-
|
|
112
|
-
actual input. `buildTemplates()` returns `files`, each with a `preview.kind` discriminator:
|
|
113
|
-
`email` contains input schema, compiled module graph, actual fixture subject/HTML/text and
|
|
114
|
-
structured diagnostics; `structured` contains the SMS/WhatsApp preview. Helper modules have no
|
|
115
|
-
file entry. An email requires at least one fixture.
|
|
151
|
+
## Dev server without a config file
|
|
116
152
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
+
```
|
|
121
160
|
|
|
122
|
-
|
|
123
|
-
|
|
161
|
+
`startDevServer` creates a Vite server with `configFile: false` and `samvaEditor()`, which is what
|
|
162
|
+
`samva templates dev` does.
|
|
124
163
|
|
|
125
164
|
## The organization brand
|
|
126
165
|
|
|
127
166
|
A project that imports `samva:brand` (the default brand) or `samva:brand/<slug>` compiles against
|
|
128
|
-
its organization's brand. The
|
|
129
|
-
|
|
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:
|
|
130
170
|
|
|
131
171
|
1. **Live.** With `SAMVA_API_KEY` in the environment or the project's `.env` (and `SAMVA_API_URL`
|
|
132
172
|
for another API), the brand is fetched directly. Without one, the `samva` CLI on `PATH` is asked
|
|
133
173
|
(`samva brands get --json`) and answers with your `samva login` session, which stays in the
|
|
134
|
-
operating-system keyring the CLI owns.
|
|
135
|
-
2. **
|
|
136
|
-
`brand-offline` warning.
|
|
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.
|
|
137
177
|
3. **Neither.** The build fails with `brand-unavailable`, naming both fixes.
|
|
138
178
|
|
|
139
|
-
When the live brand's digest differs from the
|
|
140
|
-
`brand-snapshot-stale
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
144
192
|
|
|
145
193
|
For a hosted project, commit and push the checked TSX tree with ordinary Git, then publish one exact
|
|
146
194
|
commit:
|
|
@@ -156,25 +204,23 @@ The CLI's repository-local credential helper obtains a short-lived Artifact cred
|
|
|
156
204
|
Samva's credential protocol when Git needs one. It validates the repository host/path and does not
|
|
157
205
|
store the token in Git config or the remote URL. `templates publish` requires a clean worktree and
|
|
158
206
|
an exact commit reachable from the fetched `origin` default branch; it creates an immutable
|
|
159
|
-
publication receipt and does not push or rewrite history.
|
|
160
|
-
|
|
161
|
-
Hosted builds consume the committed Bun text lockfile version 2 or npm lockfile version 3.
|
|
162
|
-
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
|
|
163
208
|
publication by default; later source edits do not change that publication.
|
|
164
209
|
|
|
165
210
|
## Local export
|
|
166
211
|
|
|
167
212
|
```ts
|
|
168
|
-
import {
|
|
213
|
+
import { exportProject } from "@samva/vite/project";
|
|
169
214
|
|
|
170
|
-
const receipt = await
|
|
215
|
+
const receipt = await exportProject({ root: ".", out: "dist/email" });
|
|
171
216
|
```
|
|
172
217
|
|
|
173
|
-
`
|
|
174
|
-
|
|
175
|
-
|
|
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.
|
|
176
221
|
|
|
177
|
-
Export writes `templates/<id>/<fixture>.html` and `.txt`,
|
|
178
|
-
and `catalog.json` with the asset mapping
|
|
179
|
-
from each exported page. For delivery,
|
|
180
|
-
there; relative URLs are for local
|
|
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 {
|
|
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
|
-
/**
|
|
6
|
-
|
|
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
|
|
11
|
-
readonly
|
|
12
|
-
readonly
|
|
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
|
-
/**
|
|
17
|
-
interface
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
readonly
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
readonly
|
|
47
|
-
/**
|
|
48
|
-
readonly
|
|
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
|
-
/**
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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,
|
|
65
|
+
export { BuildFile, BuildTemplatesOptions, BuildTemplatesResult, COMPILER, FixtureBuild, TemplateBuild, TemplateBuildSession, buildTemplates, createTemplateBuildSession };
|
|
61
66
|
//# sourceMappingURL=build.d.ts.map
|