La configurazione principale di EmDash si trova in astro.config.mjs, mentre src/live.config.ts registra il loader dei contenuti. I valori specifici del deployment possono provenire anche da variabili d’ambiente. Un piccolo blocco di metadati in package.json supporta le etichette dei template e i flussi CLI locali legacy.
Integrazione Astro
Configura EmDash come integrazione Astro in astro.config.mjs:
import { defineConfig } from "astro/config";
import emdash, { local, s3 } from "emdash/astro";
import { sqlite, libsql } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
plugins: [],
}),
],
});
Opzioni dell’integrazione
database
Obbligatoria. Configurazione dell’adattatore del database. Scegli un adattatore:
// SQLite (Node.js)
database: sqlite({ url: "file:./data.db" });
// PostgreSQL
database: postgres({ connectionString: process.env.DATABASE_URL });
// libSQL
database: libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
// Cloudflare D1 (import from @emdash-cms/cloudflare)
database: d1({ binding: "DB" });
Consulta Opzioni del database per i dettagli.
migrations
Facoltativa. Controlla la gestione a runtime delle migrazioni interne del database di EmDash. Se l’opzione viene omessa, il valore predefinito è { runtime: "auto" }.
migrations: {
runtime: "check", // "auto" | "check" | "manual"
dev: "auto", // optional development override
}
auto controlla e applica le migrazioni in sospeso, check restituisce 503 quando sono in sospeso migrazioni note alla build in esecuzione (tranne che per le richieste all’endpoint delle immagini installato da EmDash) e manual non esegue alcuna query di migrazione a runtime. EMDASH_MIGRATIONS_MODE sostituisce la modalità di runtime effettiva. Consulta Gestire le migrazioni del database del core prima di adottare check o manual.
storage
Facoltativa. Configurazione dell’adattatore di archiviazione dei media. Quando questa opzione viene omessa, EmDash archivia i file in ./.emdash/uploads e li serve tramite /_emdash/api/media/file. Scegli un adattatore quando la directory locale predefinita non è adatta:
// Local filesystem (development)
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
// R2 binding (Cloudflare Workers)
storage: r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev", // optional
});
// S3-compatible (any platform) — all fields from S3_* environment variables
storage: s3()
// Or with explicit values
storage: s3({
endpoint: "https://s3.amazonaws.com",
bucket: "my-bucket",
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
region: "us-east-1", // optional, default: "auto"
publicUrl: "https://cdn.example.com", // optional
});
Consulta Opzioni di archiviazione per i dettagli.
images
Facoltativa. Controlla se EmDash integra i media archiviati con l’ottimizzazione delle immagini di Astro. Il valore predefinito è true.
Quando è abilitata, EmDash incapsula l’endpoint delle immagini di Astro in modo che <Image> e getImage() possano leggere i byte sorgente direttamente dall’adattatore di archiviazione configurato. Funziona anche quando l’URL originale del media si trova dietro Cloudflare Access. Le richieste all’endpoint incapsulato saltano l’avvio del runtime di EmDash, quindi su di esse locals.emdash contiene solo storage, più db per un utente autenticato. locals.user viene impostato come sulle altre route pubbliche. Imposta images: false quando un altro servizio di immagini gestisce i media o quando ogni immagine deve essere renderizzata senza l’involucro dell’endpoint di EmDash.
emdash({
images: false,
});
mediaProviders
Facoltativa. Aggiunge servizi di media alla libreria multimediale. Il provider locale basato sull’archiviazione resta disponibile automaticamente; ogni descrittore in questo array aggiunge un altro luogo in cui gli editor possono sfogliare o caricare media.
L’esempio seguente aggiunge Cloudflare Images e Cloudflare Stream:
import { cloudflareImages, cloudflareStream } from "@emdash-cms/cloudflare";
emdash({
mediaProviders: [cloudflareImages({}), cloudflareStream({})],
});
Le credenziali dei provider vengono risolte a runtime. Le configurazioni vuote riportate sopra usano le variabili d’ambiente Cloudflare predefinite descritte nelle sezioni degli adattatori cloudflareImages(config) e cloudflareStream(config). Consulta Libreria multimediale: provider di media per la configurazione di binding e rendering.
objectCache
Facoltativa. Memorizza nella cache i risultati delle query di contenuti e configurazione in un archivio chiave/valore, così le letture vengono servite senza interrogare il database a ogni richiesta. Disabilitata se omessa. Scegli un adattatore:
// Cloudflare KV (shared across all isolates)
import { kvCache } from "@emdash-cms/cloudflare";
objectCache: kvCache({ binding: "CACHE" });
// In-memory (Node.js / development)
import { memoryCache } from "emdash/astro";
objectCache: memoryCache();
Consulta Cache degli oggetti per configurazione e opzioni.
middleware.outer
Facoltativa. Registra un modulo middleware di Astro all’esterno dell’intero stack di middleware di EmDash. Poiché l’integrazione lo registra con order: "pre" di Astro, viene eseguito anche prima del middleware definito in src/middleware.ts. Usalo per filtri sulle richieste o cache di risposta completa che devono evitare l’inizializzazione di runtime e database in caso di hit, oppure per header di risposta che dipendono dall’HTML finale di EmDash.
emdash({
middleware: {
outer: "./src/outer-middleware.ts",
},
});
L’ordine di esecuzione è il seguente:
- Il middleware esterno viene eseguito fino a
await next(). - EmDash inizializza runtime e database, poi esegue i middleware di setup, autenticazione e contesto della richiesta.
- La route di Astro viene renderizzata.
- EmDash applica le modifiche alla risposta, inclusi l’HTML di modifica visuale e gli header di sicurezza e di timing.
next()si risolve nel middleware esterno con quella risposta finale.
Prima di chiamare next(), il middleware dispone della normale richiesta Astro e del contesto di esecuzione della piattaforma, ma locals.emdash, locals.user, il database e lo stato di EmDash legato alla richiesta non sono disponibili. Una Response anticipata salta completamente EmDash, quindi deve includere gli header di sicurezza e di cache di cui ha bisogno. Dopo la risoluzione di next(), è sicuro finalizzare i nonce CSP, mettere in cache il corpo completo o impostare Content-Length. Se il middleware modifica il corpo, rimuovi o ricalcola qualsiasi header Content-Length esistente.
L’hook usa l’API middleware di Astro sia su Node sia su Cloudflare. Questo esempio minimo con la Cache API di Cloudflare mette in cache solo le risposte HTML anonime e restituisce gli hit prima dell’inizializzazione di EmDash:
import { waitUntil } from "cloudflare:workers";
import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware(async ({ request }, next) => {
if (request.method !== "GET" || request.headers.has("cookie")) {
return next();
}
const cacheKey = new Request(request.url, { method: "GET" });
const cached = await caches.default.match(cacheKey);
if (cached) return cached;
const response = await next();
const isHtml = response.headers.get("content-type")?.includes("text/html");
const isPrivate = response.headers.get("cache-control")?.includes("no-store");
if (response.ok && isHtml && !isPrivate) {
waitUntil(caches.default.put(cacheKey, response.clone()));
}
return response;
});
Su Node, usa la stessa struttura di middleware con una cache compatibile con Node, come Redis. Le chiavi di cache e le regole di bypass devono includere ogni proprietà della richiesta che modifica la risposta renderizzata.
playground
Facoltativa. Abilita il middleware usato dai playground EmDash usa e getta, eseguiti nel browser. Crea un database Durable Object scrivibile per ogni sessione, applica il seed configurato e autentica il visitatore come amministratore anonimo prima dell’esecuzione del normale middleware di EmDash.
import { playgroundDatabase } from "@emdash-cms/cloudflare";
emdash({
database: playgroundDatabase({ binding: "PLAYGROUND_DB" }),
playground: {
middlewareEntrypoint: "@emdash-cms/cloudflare/db/playground-middleware",
},
});
Questa modalità richiede @emdash-cms/cloudflare e un binding Durable Object. Aggira i normali middleware di setup e autenticazione, quindi usala solo per siti demo effimeri e non per un CMS di produzione.
plugins
Facoltativa. Array di plugin che vengono eseguiti nello stesso processo del sito Astro. I plugin nativi vanno qui. Anche un plugin compatibile con la sandbox può essere eseguito qui quando gli concedi fiducia con accesso completo al processo e non ti serve l’isolamento.
L’esempio seguente registra un plugin nativo:
import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];
I plugin nativi possono usare direttamente le API del server e del framework, quindi non possono essere spostati in sandboxed, a meno che il pacchetto non fornisca anche un entry point di plugin compatibile con la sandbox. Consulta Scegliere un formato di plugin per le differenze di sviluppo e distribuzione.
sandboxed
Facoltativa. Array di plugin compatibili con la sandbox che usano le API di plugin dichiarate di EmDash e vengono eseguiti in runtime isolati. Non inserire qui un plugin nativo: il codice nativo può dipendere da un accesso al processo e al framework che la sandbox non fornisce.
import thirdPartyPlugin from "third-party-emdash-plugin";
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxed: [thirdPartyPlugin()],
sandboxRunner: sandbox(),
});
I plugin in sandbox vengono ignorati quando non è configurato alcun runner di sandbox utilizzabile. Consulta Sandbox dei plugin per la configurazione del runner su Cloudflare e Node.js.
sandboxRunner
Facoltativa. Specificatore di modulo della factory che avvia i runtime isolati dei plugin. È obbligatoria per i plugin in sandboxed e per i plugin del marketplace o del registro.
Su Cloudflare Workers, usa l’adattatore sandbox():
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxRunner: sandbox(),
});
I deployment Node.js usano il modulo runner workerd documentato in Sandbox dei plugin: Node.js.
sandbox
Facoltativa. Controlla se un runner di sandbox configurato isola i plugin. La sandbox è abilitata quando sandboxRunner è configurato. Imposta sandbox: false solo per diagnosticare se un problema deriva da un plugin o dal suo runtime in sandbox:
emdash({
sandboxRunner: sandbox(),
sandbox: false,
});
Con false, i plugin dichiarati in sandboxed e quelli installati dal marketplace vengono eseguiti nel processo server principale senza isolamento né limiti di risorse. Ripristina la sandbox dopo la diagnosi.
registry
Facoltativa. Configura l’aggregatore e la policy del registro dei plugin. Senza un valore esplicito, EmDash usa https://registry.emdashcms.com quando sandboxRunner è configurato e sandbox non è false.
Imposta registry: false per disabilitare la scoperta nel registro e i plugin installati dal registro, mantenendo disponibile il runner di sandbox per i plugin dichiarati in sandboxed e per i plugin legacy del Marketplace:
emdash({
sandboxRunner: sandbox(),
registry: false,
});
Passa l’URL del servizio di registro come stringa, oppure usa un oggetto quando il sito richiede fonti di moderazione o una policy sull’età delle release. L’esempio seguente usa la forma a oggetto:
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxRunner: sandbox(),
registry: {
aggregatorUrl: "https://registry.emdashcms.com",
acceptLabelers: "did:web:labels.emdashcms.com",
policy: {
minimumReleaseAge: "48h",
minimumReleaseAgeExclude: ["did:plc:yourfirstpartydid"],
},
},
});
| Opzione | Tipo | Descrizione |
|---|---|---|
aggregatorUrl | string | URL di base del servizio di registro. Usa HTTPS in produzione. |
acceptLabelers | string | Identificatori decentralizzati (DID) facoltativi, separati da virgole, dei servizi di moderazione accettati dalla richiesta. Un DID è un identificatore stabile di account Atmosphere. Questa impostazione non può sovrascrivere la policy del servizio di registro. |
policy.minimumReleaseAge | string | number | Trattiene le release più recenti di questa età. Stringa di durata ("48h", "7d") o secondi. |
policy.minimumReleaseAgeExclude | string[] | DID dei publisher, o coppie <did>/<plugin-slug>, esentati dal trattenimento. |
La policy sull’età delle release esenta la prima release di un pacchetto solo quando il registro segnala una release conservata e conferma di aver osservato il pacchetto in modo continuativo. Un pacchetto riempito a posteriori, una release precedente eliminata o l’assenza di prove sulla cronologia mantengono il trattenimento in vigore. Le esenzioni esplicite per publisher e pacchetti si applicano indipendentemente dalla cronologia.
Consulta Il registro dei plugin per il flusso di installazione e il modello di fiducia.
marketplace
Deprecata. URL di base usato per aggiornare i plugin installati dal Marketplace legacy. La navigazione nel Marketplace e le nuove installazioni non vengono mostrate nell’amministrazione. I plugin Marketplace esistenti restano aggiornabili e disinstallabili finché questa opzione è configurata.
emdash({
marketplace: "https://marketplace.emdashcms.com",
sandboxRunner: sandbox(),
});
Gli URL di produzione devono usare HTTPS; HTTP è accettato solo per localhost e 127.0.0.1 durante lo sviluppo. Mantieni questa opzione finché ogni plugin Marketplace non è stato sostituito o disinstallato, poi rimuovila. Segui Migrare dal Marketplace per la procedura completa.
fonts
Facoltativa. Configurazione dei font dell’interfaccia di amministrazione.
Per impostazione predefinita, EmDash carica Noto Sans tramite la Font API di Astro. I font vengono scaricati da Google in fase di build e ospitati in locale, quindi non ci sono richieste CDN a runtime. Il font di base copre i sistemi di scrittura latino, cirillico, greco, devanagari e vietnamita.
Per aggiungere il supporto ad altri sistemi di scrittura, passa i nomi degli script. L’esempio seguente aggiunge arabo e giapponese:
emdash({
fonts: {
scripts: ["arabic", "japanese"],
},
})
Gli script disponibili sono arabic, armenian, bengali, chinese-simplified, chinese-traditional, chinese-hongkong, devanagari, ethiopic, farsi, georgian, gujarati, gurmukhi, hebrew, japanese, kannada, khmer, korean, lao, malayalam, myanmar, oriya, sinhala, tamil, telugu, thai e tibetan.
Ogni script corrisponde alla relativa variante di Noto Sans su Google Fonts (ad es. "arabic" carica Noto Sans Arabic). Tutti i font condividono un unico nome font-family e usano unicode-range, così il browser scarica solo i file necessari per i caratteri presenti nella pagina.
Imposta false per disabilitare completamente l’iniezione dei font e usare i font di sistema:
emdash({
fonts: false,
})
Il CSS dell’amministrazione usa la variabile CSS --font-emdash. Viene impostata automaticamente dalla configurazione dei font descritta sopra.
auth
Facoltativa. Un adattatore di autenticazione. Il login integrato di EmDash usa le passkey; impostare auth le sostituisce con un provider esterno. L’adattatore Cloudflare Access, access(), è fornito da @emdash-cms/cloudflare:
import { access } from "@emdash-cms/cloudflare";
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
roleMapping: {
Admins: 50,
Editors: 40,
},
}),
});
Opzioni di access():
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
teamDomain | string | obbligatoria | Il dominio del tuo team Cloudflare Access |
audience | string | — | Tag Audience (AUD) dell’applicazione. Su Workers, preferisci audienceEnvVar. |
audienceEnvVar | string | "CF_ACCESS_AUDIENCE" | Variabile d’ambiente da cui leggere a runtime il tag audience |
autoProvision | boolean | true | Crea un utente EmDash al primo accesso |
defaultRole | number | 30 | Livello di ruolo per gli utenti non corrispondenti a roleMapping (vedi Ruoli utente) |
syncRoles | boolean | false | Riapplica roleMapping a ogni accesso invece che solo al provisioning |
roleMapping | object | — | Associa i nomi dei gruppi dell’IdP ai livelli di ruolo di EmDash; vince la prima corrispondenza |
authProviders
Facoltativa. Un array di provider di login collegabili (a livello superiore, accanto ad auth). Ogni voce è il risultato della chiamata a una factory di provider, come mostrato di seguito:
import { github } from "emdash/auth/providers/github";
import { google } from "emdash/auth/providers/google";
import { atproto } from "@emdash-cms/auth-atproto";
emdash({
authProviders: [github(), google(), atproto()],
});
Provider integrati:
github()— leggeEMDASH_OAUTH_GITHUB_CLIENT_ID/EMDASH_OAUTH_GITHUB_CLIENT_SECRET(o i fallback senza prefisso).google()— leggeEMDASH_OAUTH_GOOGLE_CLIENT_ID/EMDASH_OAUTH_GOOGLE_CLIENT_SECRET.microsoft()— leggeEMDASH_OAUTH_MICROSOFT_CLIENT_ID/EMDASH_OAUTH_MICROSOFT_CLIENT_SECRET/EMDASH_OAUTH_MICROSOFT_TENANT_ID(o i fallback senza prefisso). Accetta{ emailVerified }. Vedi Microsoft.atproto()— login con account Atmosphere (Bluesky e l’intera rete AT Protocol). Non servono variabili d’ambiente. Accetta{ allowedDIDs, allowedHandles, defaultRole }. Vedi la guida al login Atmosphere.
I pacchetti di terze parti possono registrare i propri provider usando la stessa forma AuthProviderDescriptor — vedi Provider di login.
mcp
Facoltativa. Abilita l’endpoint Model Context Protocol (MCP) su /_emdash/api/mcp. L’endpoint è abilitato per impostazione predefinita e richiede un bearer token, quindi abilitarlo non concede accesso anonimo.
Imposta l’opzione su false quando il sito non deve esporre un endpoint MCP:
emdash({
mcp: false,
});
Consulta il Riferimento del server MCP per la creazione dei token e la configurazione dei client.
siteUrl
L’origine pubblica del sito, visibile dal browser (schema + host + porta facoltativa, senza percorso). Impostala prima di eseguire il setup di produzione, a meno che il sito non sia distribuito su Cloudflare Workers. In caso contrario, solo gli host di sviluppo in loopback possono completare il setup senza un’origine configurata.
Dietro un reverse proxy che termina il TLS, Astro.url restituisce l’indirizzo interno (http://localhost:4321) invece di quello pubblico (https://cms.example.com). Questo compromette passkey, corrispondenza dell’origine CSRF, redirect OAuth, redirect di login, discovery MCP, esportazioni di snapshot, sitemap, robots.txt e dati strutturati JSON-LD. Imposta siteUrl per correggere tutto in una volta.
L’integrazione convalida questo valore al caricamento: deve essere un URL valido con protocollo http: o https: e viene normalizzato in origine (il percorso viene rimosso).
L’esempio seguente imposta l’origine pubblica:
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
siteUrl: "https://cms.example.com",
});
Quando siteUrl non è impostata nella configurazione, EmDash controlla le variabili d’ambiente in quest’ordine: EMDASH_SITE_URL, poi SITE_URL. È utile per i deployment in container in cui l’URL pubblico viene impostato a runtime.
Il setup fallisce con SITE_URL_REQUIRED su un host non loopback quando nessuna delle due fonti è impostata. Ciò impedisce che la prima richiesta di setup non autenticata scelga l’origine usata nelle successive email di autenticazione. Una build di produzione distribuita su Cloudflare Workers è l’eccezione: Cloudflare instrada solo le richieste per gli hostname associati al Worker, quindi il setup registra https:// e l’hostname su cui viene eseguito. La stessa build servita da wrangler dev, astro preview o da workerd self-hosted su un indirizzo raggiungibile non offre questa garanzia; imposta prima lì siteUrl o EMDASH_SITE_URL.
Su Cloudflare Workers, il fallback sulle variabili d’ambiente legge process.env. Con nodejs_compat, Cloudflare popola process.env per impostazione predefinita per le compatibility date a partire dal 2025-04-01. I progetti bloccati a una data precedente devono aggiungere anche nodejs_compat_populate_process_env.
// wrangler.jsonc
{
"compatibility_date": "2026-02-24",
"compatibility_flags": ["nodejs_compat"],
"vars": { "EMDASH_SITE_URL": "https://cms.example.com" },
}
allowedOrigins
Facoltativa. Origini browser aggiuntive accettate dalla verifica delle passkey per un deployment disponibile su più di un hostname.
siteUrl definisce una singola origine canonica. Quando lo stesso deployment EmDash è raggiungibile con più hostname che condividono un dominio padre registrabile (ad es. https://example.com e https://preview.example.com), la verifica delle passkey rifiuta le asserzioni la cui origine non corrisponde esattamente a siteUrl — anche se WebAuthn consente alle passkey di essere valide tra sottodomini con lo stesso rpId.
Dichiara le origini aggiuntive accettate tramite allowedOrigins in astro.config.mjs oppure tramite la variabile d’ambiente EMDASH_ALLOWED_ORIGINS. La siteUrl canonica resta la fonte di rpId; le voci elencate qui vengono accettate al momento della verifica. Le due fonti vengono unite a runtime: la configurazione può quindi dichiarare le origini stabili (versionate, riviste nel codice), mentre l’ambiente aggiunge extra specifici dell’ambiente (ad es. anteprime effimere di PR).
L’esempio seguente dichiara un’origine aggiuntiva nella configurazione:
emdash({
siteUrl: "https://example.com",
allowedOrigins: ["https://preview.example.com"],
})
Gli stessi valori possono provenire anche da variabili d’ambiente:
EMDASH_SITE_URL=https://example.com
EMDASH_ALLOWED_ORIGINS=https://preview.example.com,https://staging.example.com
Validazione
EmDash convalida questi valori per evitare configurazioni inutili che il browser non rispetterebbe mai:
- Ogni voce deve essere un URL
http:ohttps:analizzabile, senza punto finale e senza etichette vuote nell’hostname. - Quando
allowedOriginsnon è vuoto,siteUrldeve essere impostata (da una qualsiasi delle due fonti) e non deve essere un letterale IP né avere un hostname con punto finale. - Ogni origine deve avere lo stesso hostname di
siteUrlo essere un suo sottodominio. (WebAuthn richiede cherpIdsia un suffisso registrabile di ogni origine.)
Quando la validazione fallisce, vedrai un errore con indicazione della fonte, come EmDash config error in EMDASH_ALLOWED_ORIGINS: "https://other-site.com" is not a subdomain of siteUrl "https://example.com". Allowed origins must be the same hostname as siteUrl or a subdomain of it.
Il punto in cui l’errore emerge dipende da dove sono dichiarati i valori:
- All’avvio di Astro, quando sia
config.allowedOriginssiaconfig.siteUrlprovengono daastro.config.mjs— gli errori di battitura nel codice fanno fallire la build. - Alla prima verifica di una passkey, quando uno dei due valori proviene da
EMDASH_ALLOWED_ORIGINSoEMDASH_SITE_URL— le incongruenze dell’ambiente emergono come errori 500 al primo tentativo di verifica.
Configurazione del reverse proxy
Astro riflette X-Forwarded-* solo quando l’host pubblico è consentito. Configura security.allowedDomains con l’hostname (e gli schemi) a cui accedono i tuoi utenti. In astro dev, aggiungi i corrispondenti vite.server.allowedHosts affinché Vite accetti l’header Host del proxy.
Correggi prima allowedDomains (e gli header inoltrati); usa siteUrl quando l’URL ricostruito diverge ancora dall’origine del browser (tipico quando il TLS è terminato a monte e la richiesta verso l’origine resta http://).
Con il TLS a monte, spesso basta associare il server di sviluppo al loopback (astro dev --host 127.0.0.1): il proxy si connette in locale mentre siteUrl corrisponde all’origine HTTPS pubblica.
Se il tuo proxy scrive un header con l’IP del client, imposta trustedProxyHeaders in modo che i rate limit di EmDash possano usare il vero IP del client. Senza un IP client attendibile, i rate limit di autenticazione vengono saltati e tutti gli invii pubblici di commenti condividono un unico bucket di rate limit (20 commenti ogni 10 minuti per tutti i visitatori).
La configurazione seguente imposta insieme allowedDomains, vite.server.allowedHosts e siteUrl per un deployment dietro reverse proxy:
import { defineConfig } from "astro/config";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";
export default defineConfig({
security: {
allowedDomains: [
{ hostname: "cms.example.com", protocol: "https" },
{ hostname: "cms.example.com", protocol: "http" },
],
},
vite: {
server: {
allowedHosts: ["cms.example.com"],
},
},
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
siteUrl: "https://cms.example.com",
}),
],
});
trustedProxyHeaders
Facoltativa. Header di cui fidarsi per determinare l’IP del client quando il sito gira dietro un reverse proxy che controlli. Usati dai rate limit di autenticazione (magic link, registrazione, passkey, device flow OAuth, registrazione client OAuth) e dall’endpoint pubblico dei commenti.
Su Cloudflare viene usato automaticamente l’oggetto cf allegato alla richiesta: normalmente non serve impostarla. Nei deployment self-hosted dietro nginx, Caddy, Traefik, Fly, Railway o simili, imposta qui l’header che il tuo proxy scrive, così che i rate limit possano contare le richieste per IP client reale.
L’esempio seguente si fida dell’header x-real-ip impostato da nginx, Caddy o Traefik:
emdash({
database: sqlite({ url: "file:./data.db" }),
trustedProxyHeaders: ["x-real-ip"],
});
Gli header vengono provati in ordine. I valori corrispondenti a *-forwarded-for vengono analizzati come elenchi separati da virgole e viene usata la prima voce. L’esempio seguente preferisce l’header di Fly.io e ricade su x-forwarded-for:
emdash({
trustedProxyHeaders: ["fly-client-ip", "x-forwarded-for"],
});
Quando non è impostata nella configurazione, EmDash legge la variabile d’ambiente EMDASH_TRUSTED_PROXY_HEADERS (separata da virgole). Un array vuoto esplicito nella configurazione sostituisce la variabile d’ambiente.
maxUploadSize
Facoltativa. Dimensione massima consentita, in byte, per il caricamento di un file multimediale. Si applica sia ai caricamenti multipart diretti sia a quelli tramite URL firmato. Il valore predefinito è 52_428_800 (50 MB). L’esempio seguente alza il limite a 100 MB:
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
maxUploadSize: 100 * 1024 * 1024, // 100 MB
});
| Valore | Descrizione |
|---|---|
number (byte) | Deve essere un intero finito positivo |
| omesso | Il valore predefinito è 50 MB |
I caricamenti che superano il limite configurato vengono rifiutati con una risposta 413 Payload Too Large sul percorso di caricamento diretto, oppure con 400 Validation Error sul percorso con URL firmato.
admin
Facoltativa. Sostituisce il branding di EmDash nell’interfaccia di amministrazione. Questi valori non modificano il titolo, il logo o la favicon del sito pubblico.
emdash({
admin: {
logo: "/images/agency-logo.webp",
siteName: "Agency CMS",
footerLabel: "AgencyX",
favicon: "/favicon.ico",
},
});
| Opzione | Tipo | Descrizione |
|---|---|---|
logo | string | URL o percorso del logo per la pagina di login e la barra laterale |
siteName | string | Nome mostrato nell’intestazione della barra laterale e nel titolo del browser |
footerLabel | string | false | Etichetta mostrata accanto alla versione nella barra laterale. Predefinito "EmDash"; false la nasconde |
favicon | string | URL o percorso della favicon per le pagine di amministrazione |
updateCheck
Facoltativa. Controlla l’avviso di aggiornamento del core nella dashboard di amministrazione. Il valore predefinito è true.
Quando è abilitata, EmDash interroga il registro npm pubblico per le release pubblicate di emdash — al massimo una volta al giorno, in differita dopo la risposta in modo da non rallentare mai una richiesta — e mostra un banner chiudibile nella dashboard di amministrazione quando è disponibile una versione più recente. Chiudere il banner lo nasconde per quella versione; riappare quando diventa disponibile una versione più recente.
Il banner indica la release stabile più recente pubblica da almeno 24 ore, quindi una release viene annunciata un giorno dopo la pubblicazione. Per allinearti a una policy sull’età delle release del tuo package manager, come minimumReleaseAge di pnpm, imposta minimumReleaseAge su una stringa di durata ("48h", "7d") o su un numero di secondi. Con "7d", il banner propone la release più recente che abbia almeno una settimana:
emdash({
updateCheck: { minimumReleaseAge: "7d" },
})
Il controllo è una semplice GET verso https://registry.npmjs.org/emdash e non trasporta alcun dato del sito. I deployment senza accesso a Internet in uscita semplicemente non mostrano mai il banner. Imposta false per disabilitare completamente il controllo:
emdash({
updateCheck: false,
})
Il banner è solo informativo — aggiornare EmDash significa aumentare la versione del pacchetto emdash nel tuo progetto e ripetere il deploy, cosa che l’amministrazione non può (e non dovrebbe) avviare.
toolbar
Facoltativa. Controlla come viene fornita la barra degli strumenti dell’editor (la pillola flottante sulle pagine pubbliche). Il valore predefinito è "server".
| Valore | Comportamento |
|---|---|
"server" (predefinito) | La barra degli strumenti viene iniettata lato server in ogni risposta HTML renderizzata per un editor autenticato. |
"client" | L’HTML pubblico è identico per ogni visitatore. Un minuscolo script di bootstrap mostra una pillola “Edit” nei browser che hanno effettuato l’accesso all’amministrazione; facendo clic su di essa si verifica la sessione e si ricarica la pagina con un parametro di query _edit, sempre renderizzata da zero (mai in cache) con la barra degli strumenti completa. |
false | Non renderizza mai la barra degli strumenti né lo script di bootstrap. |
emdash({
toolbar: "client",
})
Usa "client" quando il tuo HTML pubblico viene servito tramite una cache condivisa (Cloudflare Cache Everything / Workers Cache, Fastly, Varnish, …). Con l’iniezione lato server, un editor che naviga nel sito pubblico riceve la variante anonima in cache — senza la barra degli strumenti — ogni volta che un visitatore anonimo ha popolato per primo la cache; la barra degli strumenti compare e scompare quindi a seconda dello stato della cache. In modalità client non viene iniettato nulla di specifico della sessione nell’HTML condivisibile: la cache resta pienamente efficace e la barra degli strumenti è affidabile.
Note sulla modalità "client":
- I visitatori non autenticati che aprono un URL
?_editcondiviso vengono reindirizzati all’URL canonico, così il parametro non può far trapelare bozze né popolare voci di cache aggiuntive con il contenuto della pagina. - Il segnale “autenticato” è un flag
localStoragenon segreto impostato dall’amministrazione; la pillola verifica la sessione reale prima di entrare nella vista di modifica. - Il bootstrap è un piccolo
<script>inline. Se il tuo sito invia unContent-Security-Policyrigoroso senza'unsafe-inline', aggiungi un hash per esso — lo stesso vale per la barra degli strumenti iniettata lato server. - EmDash non inietta nulla di specifico della sessione — ma se i tuoi template si ramificano su
Astro.locals.user(ad es. un link di navigazione “Admin” per gli utenti autenticati), quella variazione resta nel tuo HTML e frammenta comunque la cache.
In ogni modalità, la barra degli strumenti può essere chiusa nel browser con il pulsante × (per browser, fino alla successiva apertura dell’amministrazione da parte di un editor). Le risposte di anteprima e di modalità di modifica vengono sempre renderizzate lato server con Cache-Control: private, no-store.
Adattatori di database
Importa gli adattatori da emdash/db:
import { sqlite, libsql, postgres } from "emdash/db";
sqlite(config)
Database SQLite che usa il driver di database integrato di Node.js. L’esempio seguente si connette a un file locale:
| Opzione | Tipo | Descrizione |
|---|---|---|
url | string | Percorso del file con prefisso file: |
sqlite({ url: "file:./data.db" });
libsql(config)
Database libSQL. L’esempio seguente si connette a un database libSQL remoto:
| Opzione | Tipo | Descrizione |
|---|---|---|
url | string | URL del database |
authToken | string | Token di autenticazione a runtime (facoltativo per i file locali) |
migrationAuthTokenEnv | string | Nome della variabile del token di migrazione (predefinito TURSO_AUTH_TOKEN) |
libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
postgres(config)
Database PostgreSQL con connection pooling.
| Opzione | Tipo | Descrizione |
|---|---|---|
connectionString | string | URL di connessione PostgreSQL |
host | string | Host del database |
port | number | Porta del database |
database | string | Nome del database |
user | string | Utente del database |
password | string | Password del database |
ssl | boolean | Abilita SSL |
pool.min | number | Dimensione minima del pool (predefinito: 0) |
pool.max | number | Dimensione massima del pool (predefinito: 10) |
pool.connectionTimeoutMillis | number | Attesa massima della connessione (predefinito di pg: 0, nessun timeout) |
pool.idleTimeoutMillis | number | Durata di un client inattivo (predefinito di pg: 10.000 ms) |
migrationConnectionStringEnv | string | Nome della variabile della stringa di connessione di migrazione (predefinito DATABASE_URL) |
L’esempio seguente si connette con una stringa di connessione:
postgres({ connectionString: process.env.DATABASE_URL });
d1(config)
Database Cloudflare D1. Importalo da @emdash-cms/cloudflare.
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
binding | string | — | Nome del binding D1 da wrangler.jsonc |
session | string | "disabled" | Modalità di replica in lettura: "disabled", "auto" o "primary-first" |
bookmarkCookie | string | "__em_d1_bookmark" | Nome del cookie per i bookmark di sessione |
coalesce | boolean | false | Raggruppa le letture concorrenti nello stesso turno dell’event loop; richiede una modalità di sessione diversa da "disabled" |
L’esempio seguente mostra un binding di base e uno con le repliche in lettura abilitate:
// Basic
d1({ binding: "DB" });
// With read replicas
d1({ binding: "DB", session: "auto" });
Quando session è "auto" o "primary-first", EmDash usa la D1 Sessions API per instradare le query di lettura verso repliche vicine. Gli utenti autenticati ottengono una coerenza read-your-writes basata su bookmark. Consulta Opzioni del database — Repliche in lettura per i dettagli.
hyperdrive(config?)
PostgreSQL tramite un binding Cloudflare Hyperdrive. Importa questo adattatore da @emdash-cms/cloudflare.
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
binding | string | "HYPERDRIVE" | Binding Hyperdrive principale con la cache delle query disabilitata |
cachedBinding | string | — | Secondo binding facoltativo, con cache abilitata, per le letture pubbliche anonime |
preferUncachedAfterWriteMs | number | 60_000 | Per quanto tempo le letture pubbliche usano il binding principale dopo una scrittura di contenuto quando cachedBinding è impostato |
migrationConnectionStringEnv | string | Derivato dal binding principale | Variabile d’ambiente contenente l’URL PostgreSQL diretto usato da emdash migrate |
max | number | 5 | Numero massimo di connessioni da un isolate Worker a Hyperdrive |
L’esempio seguente instrada le richieste autenticate e le scritture attraverso il binding senza cache, mentre le letture pubbliche anonime possono usare il binding con cache:
hyperdrive({
binding: "HYPERDRIVE",
cachedBinding: "HYPERDRIVE_CACHED",
preferUncachedAfterWriteMs: 60_000,
});
Entrambi i binding devono puntare allo stesso database. Installa pg versione 8.16.3 o successiva, abilita il flag di compatibilità nodejs_compat e configura un URL di database diretto per le migrazioni di deployment. Consulta Opzioni del database: Hyperdrive per la configurazione completa di Worker e migrazioni.
durableObjects(config)
Archivia il CMS in un unico Durable Object basato su SQLite. Importa questo adattatore da @emdash-cms/cloudflare.
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
binding | string | obbligatoria | Binding del namespace Durable Object per la classe EmDashDB |
name | string | "emdash" | Nome dell’oggetto singleton; modificalo solo per isolare più database dietro un unico binding |
session | string | "disabled" | "auto" instrada le letture anonime verso le repliche e le scritture verso il primario |
bookmarkCookie | string | "__em_do_bookmark" | Cookie usato per la coerenza read-your-writes in modalità "auto" |
durableObjects({ binding: "DB_DO", session: "auto" });
L’instradamento verso le repliche richiede i flag di compatibilità experimental e replica_routing, oltre alle voci di classe Durable Object e di migrazione in wrangler.jsonc.
previewDatabase(config)
Crea un database snapshot isolato per ogni sessione di anteprima in un Durable Object. L’unica opzione è il nome binding, obbligatorio:
previewDatabase({ binding: "PREVIEW_DB" });
Questo adattatore è pensato per l’infrastruttura di anteprima, non per il database principale di un sito di produzione.
playgroundDatabase(config)
Crea un database scrivibile e popolato da seed per ogni sessione di playground in un Durable Object. Abbinalo all’opzione di integrazione playground:
playgroundDatabase({ binding: "PLAYGROUND_DB" });
Il binding obbligatorio identifica il namespace Durable Object del playground. Usa questo adattatore solo per siti demo usa e getta.
Adattatori di archiviazione
Importa local e s3 da emdash/astro. L’adattatore r2 si importa da @emdash-cms/cloudflare:
import emdash, { local, s3 } from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";
local(config)
Archiviazione sul file system locale. L’esempio seguente serve i caricamenti da una directory locale:
| Opzione | Tipo | Descrizione |
|---|---|---|
directory | string | Percorso della directory |
baseUrl | string | URL di base per servire i file |
local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
r2(config)
Binding Cloudflare R2. L’esempio seguente usa un binding R2 con un URL pubblico:
| Opzione | Tipo | Descrizione |
|---|---|---|
binding | string | Nome del binding R2 |
publicUrl | string | URL pubblico facoltativo |
r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev",
});
s3(config?)
Archiviazione compatibile con S3. Tutti i campi di configurazione sono facoltativi: qualsiasi campo omesso da
s3({...}) viene risolto dalla corrispondente variabile d’ambiente S3_* all’avvio del
processo Node. I valori espliciti hanno sempre la precedenza.
Dopo l’unione dei valori di configurazione e d’ambiente, endpoint e bucket sono obbligatori. Se una delle credenziali è impostata, sono obbligatorie sia accessKeyId sia secretAccessKey. I valori mancanti fanno fallire l’avvio con il codice di errore MISSING_S3_CONFIG.
Prerequisito: installa @aws-sdk/client-s3 e @aws-sdk/s3-request-presigner
nel tuo progetto. Il core di EmDash non include l’SDK AWS. Consulta
Opzioni di archiviazione: archiviazione compatibile con S3
per i dettagli.
| Opzione | Tipo | Descrizione |
|---|---|---|
endpoint | string | URL dell’endpoint S3 (S3_ENDPOINT) |
bucket | string | Nome del bucket (S3_BUCKET) |
accessKeyId | string | Chiave di accesso (S3_ACCESS_KEY_ID) |
secretAccessKey | string | Chiave segreta (S3_SECRET_ACCESS_KEY) |
region | string | Regione, predefinita "auto" (S3_REGION) |
publicUrl | string | URL CDN facoltativo (S3_PUBLIC_URL) |
Gli esempi seguenti risolvono tutti i campi dall’ambiente, combinano configurazione e ambiente, oppure passano ogni campo esplicitamente:
// All fields from S3_* environment variables (Node container deployments)
s3()
// Mix: CDN from config, rest from environment
s3({ publicUrl: "https://cdn.example.com" })
// All explicit
s3({
endpoint: "https://xxx.r2.cloudflarestorage.com",
bucket: "media",
accessKeyId: process.env.R2_ACCESS_KEY_ID,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
publicUrl: "https://cdn.example.com",
})
La risoluzione delle variabili d’ambiente a runtime è una funzionalità esclusiva di Node. Su Cloudflare
Workers, segreti e variabili sono esposti tramite il parametro env del
fetch handler, non tramite process.env, quindi le variabili d’ambiente S3_*
non vengono rilevate. I deployment su Workers devono usare l’adattatore r2(config)
oppure passare valori espliciti a s3({...}). Consulta
Opzioni di archiviazione per i dettagli.
Adattatori della cache degli oggetti
Passa uno di questi all’opzione objectCache.
kvCache(config)
Backend Cloudflare KV, condiviso tra tutti gli isolate. Importalo da @emdash-cms/cloudflare.
kvCache({
binding: "CACHE", // KV binding name (required)
defaultTtl: 3600, // entry TTL in seconds (optional, KV minimum 60)
revalidate: 1000, // cross-isolate staleness window in ms (optional)
timeout: 2000, // per-op timeout in ms before a miss (optional, 0 disables)
keyPrefix: "em", // cache key prefix (optional)
})
memoryCache(config?)
Backend in-process per Node.js e lo sviluppo. Importalo da emdash/astro.
memoryCache({
defaultTtl: 3600, // entry TTL in seconds (optional)
revalidate: 1000, // staleness window in ms (optional)
maxEntries: 1000, // max cached keys before eviction (optional)
keyPrefix: "em", // cache key prefix (optional)
})
Consulta Cache degli oggetti per configurazione e comportamento.
Adattatori di autenticazione e sandbox
Questi adattatori restituiscono valori per le opzioni di integrazione auth e sandboxRunner.
access(config)
Sostituisce il login con passkey integrato con l’autenticazione Cloudflare Access. Importalo da @emdash-cms/cloudflare e passa il risultato a auth:
import { access } from "@emdash-cms/cloudflare";
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audienceEnvVar: "CF_ACCESS_AUDIENCE",
}),
});
teamDomain è obbligatorio. L’adattatore può leggere l’audience dell’applicazione da audience o dalla variabile indicata da audienceEnvVar; accetta inoltre autoProvision, defaultRole, syncRoles e roleMapping. L’opzione auth documenta i loro valori predefiniti e il comportamento dei ruoli.
sandbox()
Seleziona Cloudflare Worker Loader come runner della sandbox dei plugin. Importalo da @emdash-cms/cloudflare e passa il suo valore di ritorno a sandboxRunner:
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxRunner: sandbox(),
});
Il sito richiede anche un binding Worker Loader e l’entry point del bridge dei plugin. Consulta Sandbox dei plugin: Cloudflare Workers per queste impostazioni di deployment.
Adattatori dei provider di media
Passa i descrittori dei provider di media a mediaProviders. Entrambi i provider Cloudflare integrati si importano da @emdash-cms/cloudflare.
Ogni opzione *EnvVar riportata di seguito indica il nome di una variabile d’ambiente. Il provider la legge in quest’ordine: un binding Cloudflare Workers con quel nome, poi process.env sull’adattatore Node. L’opzione diretta corrispondente (accountId, accountHash, apiToken) ha sempre la precedenza su entrambi.
cloudflareImages(config)
Aggiunge Cloudflare Images per sfogliare, caricare, eliminare e distribuire asset di immagini.
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
accountId | string | Da CF_ACCOUNT_ID | ID dell’account Cloudflare |
accountIdEnvVar | string | "CF_ACCOUNT_ID" | Variabile usata quando accountId è omesso |
accountHash | string | Da CF_IMAGES_ACCOUNT_HASH | Hash dell’account usato negli URL di distribuzione |
accountHashEnvVar | string | "CF_IMAGES_ACCOUNT_HASH" | Variabile usata quando accountHash è omesso |
apiToken | string | Da CF_IMAGES_TOKEN | Token con permessi di lettura e modifica di Cloudflare Images |
apiTokenEnvVar | string | "CF_IMAGES_TOKEN" | Variabile usata quando apiToken è omesso |
deliveryDomain | string | imagedelivery.net | Hostname personalizzato per la distribuzione delle immagini |
defaultVariant | string | "public" | Variante di immagine usata per la visualizzazione |
mediaProviders: [cloudflareImages({ defaultVariant: "public" })];
cloudflareStream(config)
Aggiunge Cloudflare Stream per sfogliare, cercare, caricare, eliminare e riprodurre asset video.
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
accountId | string | Da CF_ACCOUNT_ID | ID dell’account Cloudflare |
accountIdEnvVar | string | "CF_ACCOUNT_ID" | Variabile usata quando accountId è omesso |
apiToken | string | Da CF_STREAM_TOKEN | Token con permessi di lettura e modifica di Cloudflare Stream |
apiTokenEnvVar | string | "CF_STREAM_TOKEN" | Variabile usata quando apiToken è omesso |
customerSubdomain | string | Predefinito di Cloudflare | Hostname personalizzato per la distribuzione Stream |
controls | boolean | true | Mostra i controlli del player |
autoplay | boolean | false | Avvia automaticamente la riproduzione |
loop | boolean | false | Ripete la riproduzione |
muted | boolean | false, o true con autoplay | Disattiva l’audio della riproduzione |
mediaProviders: [cloudflareStream({ controls: true })];
Consulta Libreria multimediale: provider di media per i binding e i componenti di rendering richiesti.
Live collection
Configura il loader di EmDash in src/live.config.ts:
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({
loader: emdashLoader(),
}),
};
Opzioni del loader
La funzione emdashLoader() non accetta argomenti:
emdashLoader();
Variabili d’ambiente
EmDash rispetta queste variabili d’ambiente:
| Variabile | Descrizione |
|---|---|
EMDASH_SITE_URL | Origine pubblica visibile dal browser (ricade su SITE_URL) |
EMDASH_ALLOWED_ORIGINS | Elenco, separato da virgole, di origini aggiuntive accettate dalla verifica delle passkey (deployment multi-sottodominio). |
EMDASH_DATABASE_URL | Sostituisce l’URL del database |
EMDASH_ENCRYPTION_KEY | Chiave per cifrare a riposo i segreti dei plugin. Fornita dall’operatore: non viene mai archiviata nel database. |
EMDASH_PREVIEW_SECRET | Override facoltativo del segreto HMAC dell’anteprima. Se non impostato, viene generato e archiviato nel database un valore stabile per sito. |
EMDASH_IP_SALT | Override facoltativo del salt per l’hash degli IP dei commentatori. Se non impostato, viene generato e archiviato nel database un valore stabile per sito. |
EMDASH_AUTH_SECRET | Legacy. Usato come sorgente del salt IP se impostato; le installazioni esistenti dovrebbero mantenerlo per conservare stabili gli hash degli IP dei commentatori dopo l’aggiornamento. |
EMDASH_TURNSTILE_SECRET_KEY | Chiave segreta Cloudflare Turnstile (ricade su TURNSTILE_SECRET_KEY). Quando è impostata, gli invii dei commenti devono includere un token Turnstile valido — abbinala alla prop turnstileSiteKey di <CommentForm>. |
EMDASH_URL | URL EmDash remoto per la sincronizzazione dello schema |
Genera una chiave di cifratura con il comando seguente:
npx emdash secrets generate
Configurazione di package.json
Template e siti possono dichiarare metadati facoltativi sotto una chiave emdash in package.json:
{
"emdash": {
"label": "My Blog Template",
"schema": ".emdash/schema.sql",
"seed": ".emdash/seed.json"
}
}
| Opzione | Descrizione |
|---|---|
label | Nome del template da visualizzare |
schema | Schema SQL facoltativo letto da emdash init |
seed | Percorso del file JSON di seed |
Configurazione di TypeScript
Durante lo sviluppo locale, l’integrazione Astro genera emdash-env.d.ts nella radice del progetto e lo aggiorna dopo le modifiche allo schema. Il file estende il modulo emdash, quindi gli import standard getEmDashCollection() e getEmDashEntry() deducono i campi delle collection locali senza alias di percorso.
Il comando separato emdash types recupera lo schema da un’istanza locale o remota in esecuzione e scrive per impostazione predefinita .emdash/types.ts. Aggiungi un alias solo quando il codice dell’applicazione importa direttamente quell’output autonomo:
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}
Genera i tipi autonomi dello schema remoto con il comando seguente:
npx emdash types