scribe-cms 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +150 -0
- package/dist/cli/index.cjs +2303 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +2289 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.cjs +2268 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +2238 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime.cjs +1216 -0
- package/dist/runtime.cjs.map +1 -0
- package/dist/runtime.js +1207 -0
- package/dist/runtime.js.map +1 -0
- package/dist/src/config/load-config.d.ts +10 -0
- package/dist/src/config/load-config.d.ts.map +1 -0
- package/dist/src/config/load-env.d.ts +3 -0
- package/dist/src/config/load-env.d.ts.map +1 -0
- package/dist/src/config/resolve-config.d.ts +13 -0
- package/dist/src/config/resolve-config.d.ts.map +1 -0
- package/dist/src/core/alias-helpers.d.ts +7 -0
- package/dist/src/core/alias-helpers.d.ts.map +1 -0
- package/dist/src/core/builtin-fields.d.ts +38 -0
- package/dist/src/core/builtin-fields.d.ts.map +1 -0
- package/dist/src/core/field.d.ts +60 -0
- package/dist/src/core/field.d.ts.map +1 -0
- package/dist/src/core/introspect-schema.d.ts +20 -0
- package/dist/src/core/introspect-schema.d.ts.map +1 -0
- package/dist/src/core/slug-aliases.d.ts +27 -0
- package/dist/src/core/slug-aliases.d.ts.map +1 -0
- package/dist/src/core/types.d.ts +261 -0
- package/dist/src/core/types.d.ts.map +1 -0
- package/dist/src/create-project.d.ts +10 -0
- package/dist/src/create-project.d.ts.map +1 -0
- package/dist/src/create-scribe.d.ts +11 -0
- package/dist/src/create-scribe.d.ts.map +1 -0
- package/dist/src/hash/page-hash.d.ts +7 -0
- package/dist/src/hash/page-hash.d.ts.map +1 -0
- package/dist/src/history/record-revision.d.ts +12 -0
- package/dist/src/history/record-revision.d.ts.map +1 -0
- package/dist/src/i18n/build-url.d.ts +14 -0
- package/dist/src/i18n/build-url.d.ts.map +1 -0
- package/dist/src/i18n/resolve-document.d.ts +12 -0
- package/dist/src/i18n/resolve-document.d.ts.map +1 -0
- package/dist/src/i18n/translation-index.d.ts +7 -0
- package/dist/src/i18n/translation-index.d.ts.map +1 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/loader/create-loader.d.ts +17 -0
- package/dist/src/loader/create-loader.d.ts.map +1 -0
- package/dist/src/loader/normalize-en.d.ts +3 -0
- package/dist/src/loader/normalize-en.d.ts.map +1 -0
- package/dist/src/loader/parse-mdx.d.ts +7 -0
- package/dist/src/loader/parse-mdx.d.ts.map +1 -0
- package/dist/src/redirects/build-redirects.d.ts +17 -0
- package/dist/src/redirects/build-redirects.d.ts.map +1 -0
- package/dist/src/redirects/translation-index.d.ts +4 -0
- package/dist/src/redirects/translation-index.d.ts.map +1 -0
- package/dist/src/redirects/types.d.ts +6 -0
- package/dist/src/redirects/types.d.ts.map +1 -0
- package/dist/src/runtime.d.ts +5 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/sitemap/generate-sitemap.d.ts +5 -0
- package/dist/src/sitemap/generate-sitemap.d.ts.map +1 -0
- package/dist/src/sitemap/join-base-url.d.ts +3 -0
- package/dist/src/sitemap/join-base-url.d.ts.map +1 -0
- package/dist/src/sitemap/types.d.ts +39 -0
- package/dist/src/sitemap/types.d.ts.map +1 -0
- package/dist/src/storage/sqlite.d.ts +9 -0
- package/dist/src/storage/sqlite.d.ts.map +1 -0
- package/dist/src/storage/translations.d.ts +58 -0
- package/dist/src/storage/translations.d.ts.map +1 -0
- package/dist/src/translate/gemini-client.d.ts +15 -0
- package/dist/src/translate/gemini-client.d.ts.map +1 -0
- package/dist/src/translate/page-translator.d.ts +24 -0
- package/dist/src/translate/page-translator.d.ts.map +1 -0
- package/dist/src/translate/prompts/translation-prompt.d.ts +14 -0
- package/dist/src/translate/prompts/translation-prompt.d.ts.map +1 -0
- package/dist/src/translate/resolve-translate-config.d.ts +10 -0
- package/dist/src/translate/resolve-translate-config.d.ts.map +1 -0
- package/dist/src/translate/worklist.d.ts +19 -0
- package/dist/src/translate/worklist.d.ts.map +1 -0
- package/dist/src/validate/validate-project.d.ts +16 -0
- package/dist/src/validate/validate-project.d.ts.map +1 -0
- package/dist/src/validate/validate-relations.d.ts +19 -0
- package/dist/src/validate/validate-relations.d.ts.map +1 -0
- package/dist/src/validate/validate-slug-suffix.d.ts +12 -0
- package/dist/src/validate/validate-slug-suffix.d.ts.map +1 -0
- package/dist/studio/server.cjs +673 -0
- package/dist/studio/server.cjs.map +1 -0
- package/dist/studio/server.d.ts +7 -0
- package/dist/studio/server.d.ts.map +1 -0
- package/dist/studio/server.js +664 -0
- package/dist/studio/server.js.map +1 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Scribe
|
|
2
|
+
|
|
3
|
+
Typed, file-based CMS for multilingual MDX. English source files on disk, locale translations in SQLite, Zod schemas, Gemini-powered translation, and a framework-agnostic runtime API.
|
|
4
|
+
|
|
5
|
+
Scribe has no framework dependency — it reads files and SQLite in-process and works with any Node-based stack (Next.js, Astro, Remix, SvelteKit, a static-site script, …). Examples in these docs use Next.js, but nothing about Scribe is Next-specific.
|
|
6
|
+
|
|
7
|
+
**Docs:** [Getting started](./docs/getting-started.md) · [Configuration](./docs/configuration.md) · [Writing content](./docs/content.md) · [Runtime API](./docs/runtime-api.md) · [Translation](./docs/translation.md)
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add scribe-cms zod better-sqlite3
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Set `GEMINI_API_KEY` when using `scribe translate`.
|
|
16
|
+
|
|
17
|
+
## Quickstart
|
|
18
|
+
|
|
19
|
+
### 1. Define the config
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// scribe.config.ts (at your project root)
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
25
|
+
import { z } from "zod";
|
|
26
|
+
import { defineConfig, defineContentType, field } from "scribe-cms";
|
|
27
|
+
|
|
28
|
+
const blogSchema = z.object({
|
|
29
|
+
title: field.translatable(z.string().min(1)),
|
|
30
|
+
description: field.translatable(z.string().min(50)),
|
|
31
|
+
author: field.relation("author"),
|
|
32
|
+
tags: field.structural(z.array(z.string()).default([])),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const authorSchema = z.object({
|
|
36
|
+
name: field.structural(z.string().min(1)),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export default defineConfig({
|
|
40
|
+
rootDir: path.dirname(fileURLToPath(import.meta.url)),
|
|
41
|
+
// contentDir: "content" (default)
|
|
42
|
+
// store: ".scribe/store.sqlite" (default)
|
|
43
|
+
locales: ["en", "fr"],
|
|
44
|
+
// defaultLocale: "en" (default)
|
|
45
|
+
types: [
|
|
46
|
+
defineContentType({
|
|
47
|
+
id: "blog",
|
|
48
|
+
path: "/blog/{slug}", // routable: gets URLs, sitemap, redirects
|
|
49
|
+
schema: blogSchema,
|
|
50
|
+
slugStrategy: "localized", // translated slugs per locale (default: "fixed")
|
|
51
|
+
orderBy: "-publishedAt", // default sort for list()
|
|
52
|
+
}),
|
|
53
|
+
defineContentType({
|
|
54
|
+
id: "author", // no path: reference-only type
|
|
55
|
+
contentDir: "authors", // default would be "author"
|
|
56
|
+
schema: authorSchema,
|
|
57
|
+
}),
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Content lives in `content/blog/*.mdx` and `content/authors/*.mdx`. The file
|
|
63
|
+
name is the EN slug. Frontmatter is validated against the schema; the built-in
|
|
64
|
+
fields `publishedAt`, `updatedAt`, `noindex`, `aliases`, `redirect_to`, and
|
|
65
|
+
`canonicalPath` are available on every type without declaring them.
|
|
66
|
+
|
|
67
|
+
### 2. Field markers
|
|
68
|
+
|
|
69
|
+
- `field.translatable(schema)` — sent to the translator for each locale.
|
|
70
|
+
- `field.structural(schema)` — EN-only; merged from EN into every locale document.
|
|
71
|
+
- `field.relation(typeId, options?)` — EN slug reference(s) to another type.
|
|
72
|
+
Constraints go in the options (not chained Zod methods):
|
|
73
|
+
`field.relation("glossary", { multiple: true, max: 8, optional: true })`.
|
|
74
|
+
Validated by `scribe validate`, dereferenced with `related()`.
|
|
75
|
+
|
|
76
|
+
### 3. Read content
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { createScribe } from "scribe-cms/runtime"; // bundler-safe entry; plain "scribe-cms" works in scripts
|
|
80
|
+
import config from "./scribe.config";
|
|
81
|
+
|
|
82
|
+
const scribe = createScribe(config);
|
|
83
|
+
|
|
84
|
+
// Lists & lookups
|
|
85
|
+
scribe.blog.list("fr"); // sorted docs for a locale
|
|
86
|
+
scribe.blog.get("my-post"); // exact slug lookup, no fallback
|
|
87
|
+
const r = scribe.blog.resolve("my-post", "fr"); // aliases + redirects + EN fallback
|
|
88
|
+
// r = { document, actualLocale, shouldRedirectTo?, canonicalPath? }
|
|
89
|
+
|
|
90
|
+
// Routing helpers
|
|
91
|
+
scribe.blog.staticParams(); // all { locale, slug } pairs to prerender
|
|
92
|
+
scribe.blog.alternates(doc); // hreflang map: locale → path
|
|
93
|
+
scribe.blog.translation(doc, "fr"); // the same doc in another locale (or null)
|
|
94
|
+
scribe.blog.url(doc.slug, "fr"); // path from the type's template
|
|
95
|
+
|
|
96
|
+
// Relations (fully typed from the schema)
|
|
97
|
+
scribe.blog.related(doc, "author"); // AuthorDoc — non-null, validated at build time
|
|
98
|
+
|
|
99
|
+
// Sitemap
|
|
100
|
+
await scribe.sitemap({ baseUrl: "https://example.com" }); // entries with hreflang alternates
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Typed accessors (`scribe.blog`, `scribe.author`, …) and `related()` return types
|
|
104
|
+
are inferred from the config — no codegen.
|
|
105
|
+
|
|
106
|
+
### 4. Translate & validate
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
scribe status # EN docs + translation coverage
|
|
110
|
+
scribe validate # schemas, relations, aliases, sqlite consistency
|
|
111
|
+
scribe translate --locale fr # translate stale/missing pages (Gemini)
|
|
112
|
+
scribe translate --preset active
|
|
113
|
+
scribe history blog my-post fr # revision timeline
|
|
114
|
+
scribe studio # read-only local admin UI
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Translations are stored in `.scribe/store.sqlite` keyed by a hash of the EN
|
|
118
|
+
translatable content, so `scribe translate` only re-translates what changed.
|
|
119
|
+
|
|
120
|
+
## Framework integration
|
|
121
|
+
|
|
122
|
+
Scribe runs anywhere Node does — see
|
|
123
|
+
[Runtime API → Framework integration](./docs/runtime-api.md#framework-integration).
|
|
124
|
+
The short version:
|
|
125
|
+
|
|
126
|
+
- Use `scribe-cms/runtime` in app code, `scribe-cms` in build scripts/CLI.
|
|
127
|
+
- Keep `better-sqlite3` (a native module) external to your bundler
|
|
128
|
+
(e.g. Next.js `serverExternalPackages`, Vite `ssr.external`).
|
|
129
|
+
- Gate builds: `"build": "scribe validate && <your framework build>"`.
|
|
130
|
+
- Redirects: `buildAllContentRedirects(project)` produces
|
|
131
|
+
`{ source, destination, permanent }` rules from aliases, `redirect_to`, and
|
|
132
|
+
cross-locale slugs — map them to your framework's redirect config.
|
|
133
|
+
|
|
134
|
+
## Publish to npm
|
|
135
|
+
|
|
136
|
+
From the monorepo root (builds, tests, then publishes):
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm login # once, if needed
|
|
140
|
+
pnpm publish:npm
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
From this package directory:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pnpm build
|
|
147
|
+
pnpm publish:npm # runs npm publish --access public
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Site: [scribe.genlook.app](https://scribe.genlook.app)
|