@cancia/astro 0.0.1 → 0.0.2

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.
@@ -0,0 +1,76 @@
1
+ import {
2
+ createJsonFileAdapterV2
3
+ } from "./chunk-ST44VULL.js";
4
+
5
+ // src/loader/index.ts
6
+ import { join } from "path";
7
+ function makeId(locale, entryId) {
8
+ return `${locale}/${entryId}`;
9
+ }
10
+ async function syncOnce(ctx, lists, list, site) {
11
+ ctx.store.clear();
12
+ const entries = await lists.list(site, list);
13
+ for (const entry of entries) {
14
+ const id = makeId(entry.locale, entry.id);
15
+ const data = {
16
+ ...entry.data,
17
+ locale: entry.locale,
18
+ createdAt: entry.createdAt,
19
+ updatedAt: entry.updatedAt
20
+ };
21
+ ctx.store.set({
22
+ id,
23
+ data,
24
+ digest: entry._rev
25
+ });
26
+ }
27
+ ctx.logger.info(
28
+ `cancia: loaded ${entries.length} entr${entries.length === 1 ? "y" : "ies"} from list "${list}"`
29
+ );
30
+ }
31
+ function canciaLoader(opts) {
32
+ let watcherAttached = false;
33
+ let latestCtx = null;
34
+ let pendingTimer = null;
35
+ let inflight = Promise.resolve();
36
+ return {
37
+ name: `@cancia/astro/loader[${opts.list}]`,
38
+ async load(ctx) {
39
+ latestCtx = ctx;
40
+ const storage = opts.storage ?? createJsonFileAdapterV2({
41
+ projectRoot: opts.projectRoot ?? process.cwd()
42
+ });
43
+ const { lists } = storage;
44
+ await syncOnce(ctx, lists, opts.list, opts.site);
45
+ if (ctx.watcher && !watcherAttached) {
46
+ watcherAttached = true;
47
+ const root = opts.projectRoot ?? process.cwd();
48
+ const watchDir = join(root, ".cancia", "lists", opts.list, opts.site);
49
+ ctx.watcher.add(watchDir);
50
+ const scheduleSync = () => {
51
+ if (pendingTimer) clearTimeout(pendingTimer);
52
+ pendingTimer = setTimeout(() => {
53
+ pendingTimer = null;
54
+ inflight = inflight.catch(() => {
55
+ }).then(
56
+ () => syncOnce(latestCtx, lists, opts.list, opts.site).catch((err) => {
57
+ latestCtx.logger.error(`cancia: resync failed \u2014 ${err.message}`);
58
+ })
59
+ );
60
+ }, 50);
61
+ };
62
+ const onEvent = (path) => {
63
+ if (!path.startsWith(watchDir)) return;
64
+ scheduleSync();
65
+ };
66
+ ctx.watcher.on("change", onEvent);
67
+ ctx.watcher.on("add", onEvent);
68
+ ctx.watcher.on("unlink", onEvent);
69
+ }
70
+ }
71
+ };
72
+ }
73
+
74
+ export {
75
+ canciaLoader
76
+ };
@@ -0,0 +1,63 @@
1
+ // src/storage/sqlite.ts
2
+ import { createRequire } from "module";
3
+ var require2 = createRequire(import.meta.url);
4
+ var _db = null;
5
+ function createDB(dbPath) {
6
+ const Database = require2("better-sqlite3");
7
+ const sqlite = new Database(dbPath);
8
+ sqlite.pragma("journal_mode = WAL");
9
+ sqlite.exec(`
10
+ CREATE TABLE IF NOT EXISTS cancia_content (
11
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
12
+ site TEXT NOT NULL,
13
+ key TEXT NOT NULL,
14
+ lang TEXT NOT NULL,
15
+ value TEXT NOT NULL,
16
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
17
+ UNIQUE(site, key, lang)
18
+ )
19
+ `);
20
+ return {
21
+ get: sqlite.prepare(
22
+ "SELECT value FROM cancia_content WHERE site=? AND key=? AND lang=?"
23
+ ),
24
+ set: sqlite.prepare(
25
+ `INSERT INTO cancia_content (site, key, lang, value, updated_at)
26
+ VALUES (?, ?, ?, ?, unixepoch())
27
+ ON CONFLICT(site, key, lang) DO UPDATE SET value=excluded.value, updated_at=unixepoch()`
28
+ ),
29
+ getAll: sqlite.prepare(
30
+ "SELECT key, lang, value FROM cancia_content WHERE site=?"
31
+ ),
32
+ delete: sqlite.prepare(
33
+ "DELETE FROM cancia_content WHERE site=? AND key=? AND lang=?"
34
+ )
35
+ };
36
+ }
37
+ function getDB(dbPath) {
38
+ if (!_db) _db = createDB(dbPath);
39
+ return _db;
40
+ }
41
+ function createSQLiteAdapter(dbPath) {
42
+ const path = dbPath ?? process.cwd() + "/cancia.db";
43
+ return {
44
+ async get(site, key, lang) {
45
+ const row = getDB(path).get.get(site, key, lang);
46
+ return row?.value ?? null;
47
+ },
48
+ async set(site, key, lang, value) {
49
+ getDB(path).set.run(site, key, lang, value);
50
+ },
51
+ async getAll(site) {
52
+ const rows = getDB(path).getAll.all(site);
53
+ return Object.fromEntries(rows.map((r) => [`${r.key}.${r.lang}`, r.value]));
54
+ },
55
+ async delete(site, key, lang) {
56
+ getDB(path).delete.run(site, key, lang);
57
+ }
58
+ };
59
+ }
60
+
61
+ export {
62
+ createSQLiteAdapter
63
+ };
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-NG5GJME5.js";
4
4
  import {
5
5
  describeList
6
- } from "./chunk-QAM5VKAF.js";
6
+ } from "./chunk-RKHJNQ6R.js";
7
7
 
8
8
  // src/routes/schemas.ts
9
9
  function json(body, status = 200) {
@@ -11,6 +11,23 @@ function defineList(opts) {
11
11
  validator: z.object(opts.fields)
12
12
  };
13
13
  }
14
+ var defineField = {
15
+ text: (o) => z.string().meta({ widget: "text", ...o }),
16
+ textarea: (o) => z.string().meta({ widget: "textarea", ...o }),
17
+ url: (o) => z.string().url().meta({ widget: "url", ...o }),
18
+ email: (o) => z.string().email().meta({ widget: "email", ...o }),
19
+ datetime: (o) => z.string().datetime().meta({ widget: "datetime", ...o }),
20
+ checkbox: (o) => z.boolean().meta({ widget: "checkbox", ...o }),
21
+ number: (o) => {
22
+ let n = z.number();
23
+ if (o?.min != null) n = n.min(o.min);
24
+ if (o?.max != null) n = n.max(o.max);
25
+ return n.meta({ widget: "number", ...o });
26
+ },
27
+ slug: (o) => z.string().regex(/^[a-z0-9-]+$/).meta({ widget: "slug", ...o }),
28
+ image: (o) => z.string().url().meta({ widget: "image", ...o }),
29
+ select: (o) => z.enum(o.options).meta({ widget: "select", ...o })
30
+ };
14
31
  function humanise(name) {
15
32
  return name.replace(/([A-Z])/g, " $1").replace(/[_-]/g, " ").replace(/^\w/, (c) => c.toUpperCase()).trim();
16
33
  }
@@ -44,6 +61,9 @@ function describeProperty(name, prop, required) {
44
61
  if (prop.minimum !== void 0) desc.min = prop.minimum;
45
62
  if (prop.maximum !== void 0) desc.max = prop.maximum;
46
63
  if (prop.enum) desc.options = prop.enum;
64
+ else if (prop.options) desc.options = prop.options;
65
+ if (prop.pattern !== void 0) desc.pattern = prop.pattern;
66
+ if (prop.source !== void 0) desc.source = prop.source;
47
67
  return desc;
48
68
  }
49
69
  function describeList(name, schema) {
@@ -69,5 +89,6 @@ function describeList(name, schema) {
69
89
  export {
70
90
  z,
71
91
  defineList,
92
+ defineField,
72
93
  describeList
73
94
  };
@@ -2,14 +2,6 @@ import {
2
2
  RevConflictError
3
3
  } from "./chunk-7IA5B5CF.js";
4
4
 
5
- // src/loader/index.ts
6
- import { join as join2 } from "path";
7
-
8
- // src/storage/json-file-v2.ts
9
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, rmSync } from "fs";
10
- import { dirname as dirname2, join } from "path";
11
- import { createHash, randomUUID } from "crypto";
12
-
13
5
  // src/storage/json-file.ts
14
6
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
15
7
  import { dirname } from "path";
@@ -57,6 +49,9 @@ function createJsonFileAdapter(filePath) {
57
49
  }
58
50
 
59
51
  // src/storage/json-file-v2.ts
52
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, rmSync } from "fs";
53
+ import { dirname as dirname2, join } from "path";
54
+ import { createHash, randomUUID } from "crypto";
60
55
  function canonicalize(value) {
61
56
  if (value === null || typeof value !== "object") return JSON.stringify(value);
62
57
  if (Array.isArray(value)) {
@@ -318,76 +313,7 @@ function createJsonFileAdapterV2(opts = {}) {
318
313
  };
319
314
  }
320
315
 
321
- // src/loader/index.ts
322
- function makeId(locale, entryId) {
323
- return `${locale}/${entryId}`;
324
- }
325
- async function syncOnce(ctx, lists, list, site) {
326
- ctx.store.clear();
327
- const entries = await lists.list(site, list);
328
- for (const entry of entries) {
329
- const id = makeId(entry.locale, entry.id);
330
- const data = {
331
- ...entry.data,
332
- locale: entry.locale,
333
- createdAt: entry.createdAt,
334
- updatedAt: entry.updatedAt
335
- };
336
- ctx.store.set({
337
- id,
338
- data,
339
- digest: entry._rev
340
- });
341
- }
342
- ctx.logger.info(
343
- `cancia: loaded ${entries.length} entr${entries.length === 1 ? "y" : "ies"} from list "${list}"`
344
- );
345
- }
346
- function canciaLoader(opts) {
347
- let watcherAttached = false;
348
- let latestCtx = null;
349
- let pendingTimer = null;
350
- let inflight = Promise.resolve();
351
- return {
352
- name: `@cancia/astro/loader[${opts.list}]`,
353
- async load(ctx) {
354
- latestCtx = ctx;
355
- const storage = opts.storage ?? createJsonFileAdapterV2({
356
- projectRoot: opts.projectRoot ?? process.cwd()
357
- });
358
- const { lists } = storage;
359
- await syncOnce(ctx, lists, opts.list, opts.site);
360
- if (ctx.watcher && !watcherAttached) {
361
- watcherAttached = true;
362
- const root = opts.projectRoot ?? process.cwd();
363
- const watchDir = join2(root, ".cancia", "lists", opts.list, opts.site);
364
- ctx.watcher.add(watchDir);
365
- const scheduleSync = () => {
366
- if (pendingTimer) clearTimeout(pendingTimer);
367
- pendingTimer = setTimeout(() => {
368
- pendingTimer = null;
369
- inflight = inflight.catch(() => {
370
- }).then(
371
- () => syncOnce(latestCtx, lists, opts.list, opts.site).catch((err) => {
372
- latestCtx.logger.error(`cancia: resync failed \u2014 ${err.message}`);
373
- })
374
- );
375
- }, 50);
376
- };
377
- const onEvent = (path) => {
378
- if (!path.startsWith(watchDir)) return;
379
- scheduleSync();
380
- };
381
- ctx.watcher.on("change", onEvent);
382
- ctx.watcher.on("add", onEvent);
383
- ctx.watcher.on("unlink", onEvent);
384
- }
385
- }
386
- };
387
- }
388
-
389
316
  export {
390
317
  createJsonFileAdapter,
391
- createJsonFileAdapterV2,
392
- canciaLoader
318
+ createJsonFileAdapterV2
393
319
  };
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  makeSchemasRoute
3
- } from "../chunk-YPVZDWTW.js";
3
+ } from "../chunk-KL2YMBFV.js";
4
4
  import "../chunk-NG5GJME5.js";
5
- import "../chunk-QAM5VKAF.js";
5
+ import "../chunk-RKHJNQ6R.js";
6
6
 
7
7
  // src/endpoints/schemas.ts
8
8
  import { getCanciaRuntime } from "virtual:cancia/runtime";
package/dist/index.d.ts CHANGED
@@ -4,7 +4,8 @@ export { b as CanciaKVStore, c as CanciaListStore, d as CanciaPageStore, L as Li
4
4
  import { U as UploadHandler } from './upload-DwCGjXbz.js';
5
5
  export { m as makeLocalUploadHandler } from './upload-DwCGjXbz.js';
6
6
  export { CanciaLoaderOptions, canciaLoader } from './loader/index.js';
7
- export { FieldDescription, FieldMeta, FieldWidget, ListDescription, ListSchema, SchemasModule, defineList, describeList } from './schema/index.js';
7
+ export { FieldDescription, FieldMeta, FieldMetaBase, FieldWidget, ListDescription, ListSchema, SchemasModule, defineField, defineList, describeList } from './schema/index.js';
8
+ export { createJsonFileAdapter, createJsonFileAdapterV2, createSQLiteAdapter } from './storage/index.js';
8
9
  export { z } from 'zod';
9
10
  import 'astro/loaders';
10
11
 
@@ -126,20 +127,4 @@ declare function fetchCMSData(opts: {
126
127
  */
127
128
  declare function makeUseTranslations<TUI extends Record<string, Record<string, string>>, TLang extends keyof TUI>(ui: TUI, defaultLang: TLang): (lang: TLang, cmsData?: CMSData) => (key: keyof TUI[TLang]) => string;
128
129
 
129
- declare function createJsonFileAdapter(filePath?: string): CanciaStorage;
130
-
131
- interface JsonFileV2Options {
132
- /** Project root. Defaults to process.cwd(). */
133
- projectRoot?: string;
134
- /** Override the KV file path. Defaults to <root>/cancia-content.json. */
135
- kvPath?: string;
136
- /** Override the pages file path. Defaults to <root>/.cancia/pages.json. */
137
- pagesPath?: string;
138
- /** Override the lists directory. Defaults to <root>/.cancia/lists. */
139
- listsDir?: string;
140
- }
141
- declare function createJsonFileAdapterV2(opts?: JsonFileV2Options): CanciaStorageV2;
142
-
143
- declare function createSQLiteAdapter(dbPath?: string): CanciaStorage;
144
-
145
- export { type CMSData, type CanciaIntegrationOptions, CanciaStorage, CanciaStorageV2, type R2UploadHandlerOptions, UploadHandler, canciaIntegration, createJsonFileAdapter, createJsonFileAdapterV2, createSQLiteAdapter, canciaIntegration as default, fetchCMSData, makeR2UploadHandler, makeUseTranslations };
130
+ export { type CMSData, type CanciaIntegrationOptions, CanciaStorage, CanciaStorageV2, type R2UploadHandlerOptions, UploadHandler, canciaIntegration, canciaIntegration as default, fetchCMSData, makeR2UploadHandler, makeUseTranslations };
package/dist/index.js CHANGED
@@ -3,21 +3,27 @@ import {
3
3
  } from "./chunk-22DJVJBR.js";
4
4
  import {
5
5
  makeSchemasRoute
6
- } from "./chunk-YPVZDWTW.js";
6
+ } from "./chunk-KL2YMBFV.js";
7
7
  import "./chunk-NG5GJME5.js";
8
8
  import {
9
9
  setCanciaRuntime
10
10
  } from "./chunk-DGCGIEFD.js";
11
11
  import {
12
+ defineField,
12
13
  defineList,
13
14
  describeList,
14
15
  z
15
- } from "./chunk-QAM5VKAF.js";
16
+ } from "./chunk-RKHJNQ6R.js";
17
+ import {
18
+ canciaLoader
19
+ } from "./chunk-337LJIKX.js";
20
+ import {
21
+ createSQLiteAdapter
22
+ } from "./chunk-AE4SIY24.js";
16
23
  import {
17
- canciaLoader,
18
24
  createJsonFileAdapter,
19
25
  createJsonFileAdapterV2
20
- } from "./chunk-YQDZQSES.js";
26
+ } from "./chunk-ST44VULL.js";
21
27
  import {
22
28
  RevConflictError
23
29
  } from "./chunk-7IA5B5CF.js";
@@ -634,66 +640,6 @@ function makeUseTranslations(ui, defaultLang) {
634
640
  };
635
641
  };
636
642
  }
637
-
638
- // src/storage/sqlite.ts
639
- import { createRequire } from "module";
640
- var require2 = createRequire(import.meta.url);
641
- var _db = null;
642
- function createDB(dbPath) {
643
- const Database = require2("better-sqlite3");
644
- const sqlite = new Database(dbPath);
645
- sqlite.pragma("journal_mode = WAL");
646
- sqlite.exec(`
647
- CREATE TABLE IF NOT EXISTS cancia_content (
648
- id INTEGER PRIMARY KEY AUTOINCREMENT,
649
- site TEXT NOT NULL,
650
- key TEXT NOT NULL,
651
- lang TEXT NOT NULL,
652
- value TEXT NOT NULL,
653
- updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
654
- UNIQUE(site, key, lang)
655
- )
656
- `);
657
- return {
658
- get: sqlite.prepare(
659
- "SELECT value FROM cancia_content WHERE site=? AND key=? AND lang=?"
660
- ),
661
- set: sqlite.prepare(
662
- `INSERT INTO cancia_content (site, key, lang, value, updated_at)
663
- VALUES (?, ?, ?, ?, unixepoch())
664
- ON CONFLICT(site, key, lang) DO UPDATE SET value=excluded.value, updated_at=unixepoch()`
665
- ),
666
- getAll: sqlite.prepare(
667
- "SELECT key, lang, value FROM cancia_content WHERE site=?"
668
- ),
669
- delete: sqlite.prepare(
670
- "DELETE FROM cancia_content WHERE site=? AND key=? AND lang=?"
671
- )
672
- };
673
- }
674
- function getDB(dbPath) {
675
- if (!_db) _db = createDB(dbPath);
676
- return _db;
677
- }
678
- function createSQLiteAdapter(dbPath) {
679
- const path = dbPath ?? process.cwd() + "/cancia.db";
680
- return {
681
- async get(site, key, lang) {
682
- const row = getDB(path).get.get(site, key, lang);
683
- return row?.value ?? null;
684
- },
685
- async set(site, key, lang, value) {
686
- getDB(path).set.run(site, key, lang, value);
687
- },
688
- async getAll(site) {
689
- const rows = getDB(path).getAll.all(site);
690
- return Object.fromEntries(rows.map((r) => [`${r.key}.${r.lang}`, r.value]));
691
- },
692
- async delete(site, key, lang) {
693
- getDB(path).delete.run(site, key, lang);
694
- }
695
- };
696
- }
697
643
  export {
698
644
  RevConflictError,
699
645
  canciaIntegration,
@@ -702,6 +648,7 @@ export {
702
648
  createJsonFileAdapterV2,
703
649
  createSQLiteAdapter,
704
650
  canciaIntegration as default,
651
+ defineField,
705
652
  defineList,
706
653
  describeList,
707
654
  fetchCMSData,
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  canciaLoader
3
- } from "../chunk-YQDZQSES.js";
3
+ } from "../chunk-337LJIKX.js";
4
+ import "../chunk-ST44VULL.js";
4
5
  import "../chunk-7IA5B5CF.js";
5
6
  export {
6
7
  canciaLoader
@@ -15,20 +15,43 @@ export { z } from 'zod';
15
15
  * z.boolean() → "checkbox"
16
16
  * z.enum([...]) → "select"
17
17
  */
18
- type FieldWidget = "text" | "textarea" | "url" | "email" | "datetime" | "number" | "checkbox" | "select" | "image";
18
+ type FieldWidget = "text" | "textarea" | "url" | "email" | "datetime" | "number" | "checkbox" | "select" | "image" | "slug";
19
19
  /**
20
- * Metadata attached to a Zod field via `.meta({...})`. All optional.
20
+ * Fields common to every widget's metadata. All optional.
21
21
  */
22
- interface FieldMeta {
22
+ interface FieldMetaBase {
23
23
  /** Display label for the field. Defaults to humanised field name. */
24
24
  label?: string;
25
25
  /** Help text shown under the input. */
26
26
  description?: string;
27
- /** Override widget choice. */
28
- widget?: FieldWidget;
29
27
  /** Placeholder text in the input. */
30
28
  placeholder?: string;
31
29
  }
30
+ /**
31
+ * Metadata attached to a Zod field via `.meta({...})`.
32
+ *
33
+ * This is a widget-keyed discriminated union: each widget variant only
34
+ * permits the options that are legal for it. It exists at the type level
35
+ * only — Zod's `.meta()` accepts any object (its `GlobalMeta` carries an
36
+ * index signature), so this union is what `defineField` uses to type its
37
+ * per-widget option parameters. Passing an illegal option to a
38
+ * `defineField.*` helper is therefore a compile error, while bare
39
+ * `z.string().meta({ widget: "textarea" })` calls stay valid.
40
+ *
41
+ * Later plans extend this union with array / reference / richtext variants.
42
+ */
43
+ type FieldMeta = (FieldMetaBase & {
44
+ widget?: "text" | "textarea" | "url" | "email" | "number" | "checkbox" | "datetime";
45
+ }) | (FieldMetaBase & {
46
+ widget: "select";
47
+ options?: string[];
48
+ }) | (FieldMetaBase & {
49
+ widget: "image";
50
+ alt?: boolean;
51
+ }) | (FieldMetaBase & {
52
+ widget: "slug";
53
+ source?: string;
54
+ });
32
55
  interface ListSchemaOptions<TFields extends Record<string, z.ZodTypeAny>> {
33
56
  /** Display label for the list (e.g. "Blog Posts"). */
34
57
  label: string;
@@ -79,7 +102,53 @@ interface FieldDescription {
79
102
  /** For number widgets: min/max. */
80
103
  min?: number;
81
104
  max?: number;
105
+ /** For string widgets: a regex pattern (source, no delimiters) the value must match. */
106
+ pattern?: string;
107
+ /** For slug widgets: the field name to auto-generate the slug from. */
108
+ source?: string;
82
109
  }
110
+ /**
111
+ * Typed field constructors. Each returns the underlying Zod type with a
112
+ * correctly-typed `.meta()` already attached, so schemas read declaratively:
113
+ *
114
+ * import { defineField as f } from "@cancia/astro/schema";
115
+ *
116
+ * fields: {
117
+ * title: f.text({ label: "Title" }),
118
+ * slug: f.slug({ source: "title", label: "URL slug" }),
119
+ * status: f.select({ options: ["draft", "published"], label: "Status" }),
120
+ * }
121
+ *
122
+ * The option parameter of each helper is derived from the matching FieldMeta
123
+ * variant, so illegal options (e.g. passing `source` to `text`) are compile
124
+ * errors. `defineField` is purely additive — bare `z.string().meta({...})`
125
+ * declarations keep working unchanged.
126
+ */
127
+ /** Options accepted by the plain string/number/etc. widgets (no extras). */
128
+ type PlainFieldOptions = FieldMetaBase;
129
+ declare const defineField: {
130
+ text: (o?: PlainFieldOptions) => z.ZodString;
131
+ textarea: (o?: PlainFieldOptions) => z.ZodString;
132
+ url: (o?: PlainFieldOptions) => z.ZodString;
133
+ email: (o?: PlainFieldOptions) => z.ZodString;
134
+ datetime: (o?: PlainFieldOptions) => z.ZodString;
135
+ checkbox: (o?: PlainFieldOptions) => z.ZodBoolean;
136
+ number: (o?: PlainFieldOptions & {
137
+ min?: number;
138
+ max?: number;
139
+ }) => z.ZodNumber;
140
+ slug: (o: FieldMetaBase & {
141
+ source?: string;
142
+ }) => z.ZodString;
143
+ image: (o?: FieldMetaBase & {
144
+ alt?: boolean;
145
+ }) => z.ZodString;
146
+ select: (o: FieldMetaBase & {
147
+ options: string[];
148
+ }) => z.ZodEnum<{
149
+ [x: string]: string;
150
+ }>;
151
+ };
83
152
  interface ListDescription {
84
153
  name: string;
85
154
  label: string;
@@ -93,4 +162,4 @@ declare function describeList(name: string, schema: ListSchema): ListDescription
93
162
  /** Map of list name → schema. What users export from src/cms/schemas.ts. */
94
163
  type SchemasModule = Record<string, ListSchema>;
95
164
 
96
- export { type FieldDescription, type FieldMeta, type FieldWidget, type ListDescription, type ListSchema, type ListSchemaOptions, type SchemasModule, defineList, describeList };
165
+ export { type FieldDescription, type FieldMeta, type FieldMetaBase, type FieldWidget, type ListDescription, type ListSchema, type ListSchemaOptions, type SchemasModule, defineField, defineList, describeList };
@@ -1,9 +1,11 @@
1
1
  import {
2
+ defineField,
2
3
  defineList,
3
4
  describeList,
4
5
  z
5
- } from "../chunk-QAM5VKAF.js";
6
+ } from "../chunk-RKHJNQ6R.js";
6
7
  export {
8
+ defineField,
7
9
  defineList,
8
10
  describeList,
9
11
  z
@@ -0,0 +1,20 @@
1
+ import { C as CanciaStorage, a as CanciaStorageV2 } from '../types-BMlLS-OS.js';
2
+ export { b as CanciaKVStore, c as CanciaListStore, d as CanciaPageStore, L as ListEntry, P as PageMeta, e as PageRecord, f as PageSEO, R as Rev, g as RevConflictError } from '../types-BMlLS-OS.js';
3
+
4
+ declare function createJsonFileAdapter(filePath?: string): CanciaStorage;
5
+
6
+ interface JsonFileV2Options {
7
+ /** Project root. Defaults to process.cwd(). */
8
+ projectRoot?: string;
9
+ /** Override the KV file path. Defaults to <root>/cancia-content.json. */
10
+ kvPath?: string;
11
+ /** Override the pages file path. Defaults to <root>/.cancia/pages.json. */
12
+ pagesPath?: string;
13
+ /** Override the lists directory. Defaults to <root>/.cancia/lists. */
14
+ listsDir?: string;
15
+ }
16
+ declare function createJsonFileAdapterV2(opts?: JsonFileV2Options): CanciaStorageV2;
17
+
18
+ declare function createSQLiteAdapter(dbPath?: string): CanciaStorage;
19
+
20
+ export { CanciaStorage, CanciaStorageV2, createJsonFileAdapter, createJsonFileAdapterV2, createSQLiteAdapter };
@@ -0,0 +1,16 @@
1
+ import {
2
+ createSQLiteAdapter
3
+ } from "../chunk-AE4SIY24.js";
4
+ import {
5
+ createJsonFileAdapter,
6
+ createJsonFileAdapterV2
7
+ } from "../chunk-ST44VULL.js";
8
+ import {
9
+ RevConflictError
10
+ } from "../chunk-7IA5B5CF.js";
11
+ export {
12
+ RevConflictError,
13
+ createJsonFileAdapter,
14
+ createJsonFileAdapterV2,
15
+ createSQLiteAdapter
16
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cancia/astro",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Astro integration for Cancia CMS — inline editing with zero separate server",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -23,6 +23,10 @@
23
23
  "./loader": {
24
24
  "types": "./dist/loader/index.d.ts",
25
25
  "import": "./dist/loader/index.js"
26
+ },
27
+ "./storage": {
28
+ "types": "./dist/storage/index.d.ts",
29
+ "import": "./dist/storage/index.js"
26
30
  }
27
31
  },
28
32
  "files": [
@@ -46,13 +50,15 @@
46
50
  "better-sqlite3": "^11.0.0",
47
51
  "tsup": "^8.0.0",
48
52
  "typescript": "^5.4.0",
49
- "vite": "^8.0.0"
53
+ "vite": "^8.0.0",
54
+ "vitest": "^4.1.9"
50
55
  },
51
56
  "dependencies": {
52
57
  "zod": "^4.4.3"
53
58
  },
54
59
  "scripts": {
55
60
  "build": "tsup",
56
- "typecheck": "tsc --noEmit"
61
+ "typecheck": "tsc --noEmit",
62
+ "test": "vitest run"
57
63
  }
58
64
  }