@alstar/studio 0.0.0-beta.1 → 0.0.0-beta.10

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.
Files changed (58) hide show
  1. package/api/api-key.ts +73 -0
  2. package/api/backup.ts +38 -0
  3. package/api/block.ts +96 -30
  4. package/api/index.ts +12 -1
  5. package/api/mcp.ts +53 -0
  6. package/components/AdminPanel.ts +74 -0
  7. package/components/Backup.ts +10 -0
  8. package/components/BlockFieldRenderer.ts +121 -0
  9. package/components/BlockRenderer.ts +22 -0
  10. package/components/Entries.ts +19 -11
  11. package/components/Entry.ts +13 -21
  12. package/components/FieldRenderer.ts +35 -0
  13. package/components/Render.ts +46 -0
  14. package/components/Settings.ts +101 -0
  15. package/components/{layout.ts → SiteLayout.ts} +22 -17
  16. package/components/fields/Markdown.ts +44 -0
  17. package/components/fields/Text.ts +42 -0
  18. package/components/fields/index.ts +4 -0
  19. package/components/icons.ts +100 -7
  20. package/index.ts +66 -34
  21. package/package.json +8 -8
  22. package/pages/entry/[id].ts +17 -0
  23. package/{components → pages}/index.ts +7 -4
  24. package/pages/settings.ts +10 -0
  25. package/public/studio/css/admin-panel.css +103 -0
  26. package/public/studio/css/blocks-field.css +53 -0
  27. package/public/studio/css/settings.css +24 -0
  28. package/public/studio/js/markdown-editor.js +34 -0
  29. package/public/studio/js/sortable-list.js +50 -0
  30. package/public/studio/main.css +161 -0
  31. package/public/studio/main.js +9 -0
  32. package/queries/block-with-children.ts +74 -0
  33. package/queries/block.ts +289 -0
  34. package/queries/db-types.ts +15 -0
  35. package/queries/getBlockTrees-2.ts +0 -0
  36. package/queries/getBlockTrees.ts +316 -0
  37. package/queries/getBlocks.ts +214 -0
  38. package/queries/index.ts +2 -98
  39. package/queries/structure-types.ts +97 -0
  40. package/readme.md +205 -0
  41. package/schemas.ts +15 -54
  42. package/types.ts +133 -5
  43. package/utils/buildBlocksTree.ts +4 -4
  44. package/utils/define.ts +41 -0
  45. package/utils/file-based-router.ts +11 -2
  46. package/utils/get-config.ts +8 -9
  47. package/utils/get-or-create-row.ts +41 -0
  48. package/utils/html.ts +247 -0
  49. package/utils/startup-log.ts +10 -0
  50. package/components/AdminPanel/AdminPanel.css +0 -59
  51. package/components/AdminPanel/AdminPanel.ts +0 -57
  52. package/components/Block.ts +0 -116
  53. package/components/Entry.css +0 -7
  54. package/components/Field.ts +0 -164
  55. package/components/Fields.ts +0 -43
  56. package/public/main.css +0 -92
  57. package/public/main.js +0 -43
  58. /package/public/{favicon.svg → studio/favicon.svg} +0 -0
@@ -0,0 +1,214 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { sql } from "../utils/sql.ts";
3
+
4
+ // --- Field & block definitions ---
5
+ type FieldType = 'text' | 'slug' | 'markdown' | 'blocks' | 'image';
6
+
7
+ interface BaseField {
8
+ label: string;
9
+ type: FieldType;
10
+ }
11
+
12
+ interface TextField extends BaseField {
13
+ type: 'text' | 'slug' | 'markdown';
14
+ }
15
+
16
+ interface ImageField extends BaseField {
17
+ type: 'image';
18
+ }
19
+
20
+ interface BlocksField extends BaseField {
21
+ type: 'blocks';
22
+ children: Record<string, BlockDef>;
23
+ }
24
+
25
+ type FieldDef = TextField | ImageField | BlocksField;
26
+
27
+ interface BlockDef {
28
+ label: string;
29
+ type: string;
30
+ fields: Record<string, FieldDef>;
31
+ }
32
+
33
+ // --- Identity helpers (preserve literal types) ---
34
+ export function defineField<T extends FieldDef>(field: T) {
35
+ return field;
36
+ }
37
+
38
+ export function defineBlock<T extends BlockDef>(block: T) {
39
+ return block;
40
+ }
41
+
42
+ export function defineStructure<T extends Record<string, BlockDef>>(structure: T) {
43
+ return structure;
44
+ }
45
+
46
+ // --- Type mapping from structure to data ---
47
+ type FieldToType<F extends FieldDef> =
48
+ F['type'] extends 'text' | 'slug' | 'markdown' ? string :
49
+ F['type'] extends 'image' ? string :
50
+ F['type'] extends 'blocks'
51
+ ? {
52
+ [K in keyof F['children']]: {
53
+ type: K;
54
+ data: BlockData<F['children'][K]>;
55
+ }
56
+ }[keyof F['children']][]
57
+ : never;
58
+
59
+ type BlockData<B extends BlockDef> = {
60
+ [K in keyof B['fields']]: FieldToType<B['fields'][K]>;
61
+ };
62
+
63
+ type StructureData<S extends Record<string, BlockDef>> = {
64
+ [K in keyof S]: BlockData<S[K]>;
65
+ };
66
+
67
+ // This will be inferred after you define your structure
68
+ export type CMSData<S extends Record<string, BlockDef>> = StructureData<S>;
69
+
70
+
71
+ interface DBBase {
72
+ id: number;
73
+ created_at: string;
74
+ updated_at: string;
75
+ name: string;
76
+ label: string;
77
+ type: string;
78
+ sort_order: number;
79
+ value: string | null;
80
+ options: any;
81
+ status: string;
82
+ parent_id: number | null;
83
+ }
84
+
85
+ export function buildTypedForest<
86
+ S extends Record<string, BlockDef>,
87
+ K extends keyof S
88
+ >(
89
+ rows: DBBase[],
90
+ rootDef: S[K]
91
+ ): CMSData<S>[K][] {
92
+ const map = new Map<number, DBBase & { children: DBBase[] }>();
93
+
94
+ // Initialize with children arrays
95
+ for (const r of rows) {
96
+ map.set(r.id, { ...r, children: [] });
97
+ }
98
+
99
+ // Link children to parents
100
+ const roots: (DBBase & { children: DBBase[] })[] = [];
101
+ for (const r of rows) {
102
+ const node = map.get(r.id)!;
103
+ if (r.parent_id === null) {
104
+ roots.push(node);
105
+ } else {
106
+ const parent = map.get(r.parent_id);
107
+ if (parent) parent.children.push(node);
108
+ }
109
+ }
110
+
111
+ // Recursive transformer: maps a DB row into typed data
112
+ function transformNode<D extends FieldDef | BlockDef>(
113
+ node: DBBase & DBBase['type'] extends 'blocks' ? { children: DBBase[] } : {},
114
+ def: D
115
+ ): any {
116
+ if ('fields' in def) {
117
+ // It's a BlockDef
118
+ const result: any = {};
119
+ for (const key in def.fields) {
120
+ const fieldDef = def.fields[key];
121
+ const childNode = node.children.find(c => c.name === key);
122
+ result[key] = childNode
123
+ ? transformNode(childNode, fieldDef)
124
+ : getDefaultValue(fieldDef);
125
+ }
126
+ return result;
127
+ } else {
128
+ // It's a FieldDef
129
+ if (def.type === 'text' || def.type === 'slug' || def.type === 'markdown') {
130
+ return node.value ?? '';
131
+ }
132
+ if (def.type === 'image') {
133
+ return node.value ?? '';
134
+ }
135
+ if (def.type === 'blocks') {
136
+ return node.children.map(child => {
137
+ const childDef = def.children[child.name as keyof typeof def.children];
138
+ return {
139
+ type: child.name as keyof typeof def.children,
140
+ data: transformNode(child, childDef)
141
+ };
142
+ });
143
+ }
144
+ }
145
+ }
146
+
147
+ // Provide safe defaults for missing fields
148
+ function getDefaultValue(fieldDef: FieldDef): any {
149
+ if (fieldDef.type === 'blocks') return [];
150
+ return '';
151
+ }
152
+
153
+ // Map all root nodes into typed data
154
+ return roots.map(root => transformNode(root, rootDef));
155
+ }
156
+
157
+
158
+ function rootQuery(filterSql: string, depthLimit?: number) {
159
+ const depthLimitClause =
160
+ depthLimit !== undefined ? `WHERE d.depth + 1 <= ${depthLimit}` : '';
161
+
162
+ return sql`
163
+ with recursive
164
+ ancestors as (
165
+ select
166
+ id, created_at, updated_at, name, label, type, sort_order, value, options, status, parent_id,
167
+ 0 as depth
168
+ from blocks
169
+ where ${filterSql}
170
+ union all
171
+ select
172
+ b.id, b.created_at, b.updated_at, b.name, b.label, b.type, b.sort_order, b.value, b.options, b.status, b.parent_id,
173
+ a.depth + 1
174
+ from blocks b
175
+ inner join ancestors a on b.id = a.parent_id
176
+ ),
177
+ roots as (
178
+ select * from ancestors where parent_id is null
179
+ ),
180
+ descendants as (
181
+ select * from roots
182
+ union all
183
+ select
184
+ b.id, b.created_at, b.updated_at, b.name, b.label, b.type, b.sort_order, b.value, b.options, b.status, b.parent_id,
185
+ d.depth + 1
186
+ from blocks b
187
+ inner join descendants d on b.parent_id = d.id ${depthLimitClause}
188
+ )
189
+ select * from descendants
190
+ order by parent_id, sort_order
191
+ `;
192
+ }
193
+
194
+ export function queryTypedRoot<
195
+ S extends Record<string, BlockDef>,
196
+ K extends keyof S
197
+ >(
198
+ db: DatabaseSync,
199
+ structure: S,
200
+ blockType: K,
201
+ opts: { id?: number; depthLimit?: number }
202
+ ): CMSData<S>[K][] {
203
+ let filterSql: string;
204
+ if (opts.id !== undefined) {
205
+ filterSql = `id = ${opts.id}`;
206
+ } else {
207
+ filterSql = `type = '${String(structure[blockType].type)}'`;
208
+ }
209
+
210
+ const sql = rootQuery(filterSql, opts.depthLimit);
211
+ const rows = db.prepare(sql).all() as unknown as DBBase[];
212
+
213
+ return buildTypedForest(rows, structure[blockType]);
214
+ }
package/queries/index.ts CHANGED
@@ -1,98 +1,2 @@
1
- import { db } from '@alstar/db'
2
- import { sql } from '../utils/sql.ts'
3
- import { type DBBlock } from '../types.ts'
4
- import { buildBlockTree } from '../utils/buildBlocksTree.ts'
5
-
6
- export const blocks = (options: {
7
- parent: string | number | null
8
- }): DBBlock[] | null => {
9
- const q =
10
- options.parent === null
11
- ? sql`
12
- select
13
- *
14
- from
15
- blocks
16
- where
17
- parent_block_id is null;
18
- `
19
- : sql`
20
- select
21
- *
22
- from
23
- blocks
24
- where
25
- parent_block_id = ?;
26
- `
27
-
28
- const transaction = db.database.prepare(q)
29
-
30
- if (options.parent === null) {
31
- return transaction.all() as unknown as DBBlock[]
32
- } else {
33
- return transaction.all(options.parent) as unknown as DBBlock[]
34
- }
35
- }
36
-
37
- export const block = (
38
- query: Record<string, string | null>,
39
- options?: { recursive: boolean },
40
- ): DBBlock | null => {
41
- const str = Object.keys(query)
42
- .map((key) => `${key.replace('parent', 'parent_block_id')} = ?`)
43
- .join(' AND ')
44
-
45
- const q = options?.recursive
46
- ? sql`
47
- with recursive
48
- block_hierarchy as (
49
- -- Anchor member: the root block, depth = 0
50
- select
51
- b.*,
52
- 0 as depth
53
- from
54
- blocks b
55
- where
56
- b.id = ? -- Replace 5 with your root block ID
57
- union all
58
- -- Recursive member: find children and increment depth
59
- select
60
- b.*,
61
- bh.depth + 1 as depth
62
- from
63
- blocks b
64
- inner join block_hierarchy bh on b.parent_block_id = bh.id
65
- )
66
- select
67
- *
68
- from
69
- block_hierarchy
70
- order by
71
- sort_order;
72
- `
73
- : sql`
74
- select
75
- *
76
- from
77
- blocks
78
- where
79
- ${str};
80
- `
81
-
82
- const transaction = db.database.prepare(q)
83
-
84
- try {
85
- if (options?.recursive) {
86
- const res = transaction.all(query.id) as unknown as any[]
87
- return (buildBlockTree(res) as any) || null
88
- }
89
- return (
90
- (transaction.get(...Object.values(query)) as unknown as DBBlock) || null
91
- )
92
- } catch (error) {
93
- console.log('error')
94
- return null
95
- }
96
- }
97
-
98
- export const query = { blocks, block }
1
+ export * as query from './block.ts'
2
+ export { getBlockTrees } from './getBlockTrees.ts'
@@ -0,0 +1,97 @@
1
+ // structure-types.ts
2
+ import { type DBBase } from "./db-types.ts";
3
+
4
+ /**
5
+ * Extract helpers
6
+ */
7
+ type ArrayElement<T> = T extends readonly (infer U)[] ? U : never;
8
+
9
+ /**
10
+ * Field definition shape inferred from defineField(...) (the runtime helper)
11
+ * We keep it generic as "any" shape but with the important properties present
12
+ */
13
+ type FieldDef = {
14
+ readonly name: string;
15
+ readonly type: string;
16
+ readonly fields?: readonly any[]; // only present when type === 'blocks'
17
+ };
18
+
19
+ /**
20
+ * Block definition shape inferred from defineBlock(...) (the runtime helper)
21
+ */
22
+ type BlockDef = {
23
+ readonly name: string;
24
+ readonly type: string;
25
+ readonly fields?: readonly FieldDef[];
26
+ };
27
+
28
+ /**
29
+ * Primitive field node (non-'blocks'): DBBase + kept fields but no children
30
+ */
31
+ type PrimitiveFieldNode<TField extends FieldDef> =
32
+ DBBase & {
33
+ readonly name: TField["name"];
34
+ readonly type: TField["type"];
35
+ // no children (leaf), no nested fields
36
+ readonly children?: [];
37
+ readonly fields?: {}; // empty object for leaf
38
+ };
39
+
40
+ /**
41
+ * For 'blocks' typed field, we need:
42
+ * - the block node representing the 'blocks' wrapper (has DBBase props)
43
+ * - its 'children' are an array of BlockNodes corresponding to nested block defs supplied in the field's 'fields' array
44
+ * - its 'fields' property is the mapping of its own child-field names (can be empty)
45
+ */
46
+ type BlocksFieldNode<
47
+ TField extends FieldDef,
48
+ TFieldDefs extends readonly BlockDef[]
49
+ > = DBBase & {
50
+ readonly name: TField["name"]; // e.g. "blocks" or "images"
51
+ readonly type: "blocks"; // literally 'blocks'
52
+ readonly children: BlockNodeFromBlockDefs<TFieldDefs>[]; // children are instances of the nested blocks
53
+ readonly fields: FieldsFromFieldDefs<TFieldDefs[number]["fields"]>; // the blocks-wrapper's own fields mapping (if any)
54
+ };
55
+
56
+ /**
57
+ * Build the 'fields' object for a set of FieldDef[].
58
+ * Maps each field name -> either PrimitiveFieldNode or BlocksFieldNode recursively.
59
+ */
60
+ type FieldsFromFieldDefs<TDefs> =
61
+ // If no fields
62
+ TDefs extends readonly any[]
63
+ ? {
64
+ // For each field F in TDefs, map F['name'] -> node type
65
+ [F in ArrayElement<TDefs> as F extends { name: infer N extends string } ? N : never]:
66
+ F extends { type: "blocks"; fields: readonly BlockDef[] }
67
+ ? BlocksFieldNode<F, F["fields"]>
68
+ : PrimitiveFieldNode<F>;
69
+ }
70
+ : {};
71
+
72
+ /**
73
+ * A Block node type for a particular BlockDef.
74
+ * - fields: mapping derived from the block's declared fields
75
+ * - children: by default [], because in our final shape all immediate children are placed under 'fields' of the parent.
76
+ * BUT for nodes that are themselves 'blocks' wrappers (i.e. appear as a Block instance of a nested block def),
77
+ * their 'children' will contain actual child blocks (these are handled via BlocksFieldNode above).
78
+ */
79
+ export type BlockNode<T extends BlockDef> = DBBase & {
80
+ readonly name: T["name"];
81
+ readonly type: T["type"];
82
+ readonly fields: FieldsFromFieldDefs<T["fields"]>;
83
+ // for regular block nodes, children will usually be [] (top-level parent's children moved into fields)
84
+ readonly children: [];
85
+ };
86
+
87
+ /**
88
+ * Construct BlockNode unions for a set of block defs (used when blocks field has multiple block subdefs)
89
+ */
90
+ type BlockNodeFromBlockDefs<TDefs extends readonly BlockDef[]> =
91
+ ArrayElement<TDefs> extends infer B ? (B extends BlockDef ? BlockNode<B> : never) : never;
92
+
93
+ /**
94
+ * The top-level forest return type when you pass a structure: it's an array of BlockNode of any top-level BlockDef
95
+ */
96
+ export type BlockTreeFromStructure<TStructure extends readonly BlockDef[]> =
97
+ BlockNodeFromBlockDefs<TStructure>;
package/readme.md ADDED
@@ -0,0 +1,205 @@
1
+ # Alstar Studio
2
+
3
+ Alstar Studio is a **fullstack framework** for building CMS-driven applications with **native Node.js** and **Hono**.
4
+
5
+
6
+ ## Installation
7
+
8
+ Create a new project:
9
+
10
+ ```sh
11
+ pnpm create @alstar
12
+ ```
13
+
14
+ Follow the CLI prompts to set up a starter project in your chosen folder.
15
+
16
+
17
+ ## Development
18
+
19
+ Start the dev server:
20
+
21
+ ```sh
22
+ pnpm run dev
23
+ ```
24
+
25
+ This runs a **Hono server**.
26
+
27
+ The core app is created via `createStudio(structure)`, which returns the Hono app. This makes it possible to extend the server with plugins or custom settings:
28
+
29
+ ```ts
30
+ const app = await createStudio(structure)
31
+ // app.use(...) custom middleware
32
+ ```
33
+
34
+
35
+ ## Routing
36
+
37
+ Pages are defined in the `/pages` directory.
38
+
39
+ * Each `.ts` file becomes a route.
40
+ * Dynamic routes are created with square brackets, e.g. `/pages/[slug].ts`.
41
+
42
+
43
+ ## CMS
44
+
45
+ Access the CMS at:
46
+
47
+ ```
48
+ /admin
49
+ ```
50
+
51
+ ### Defining Content Structure
52
+
53
+ Pass a `Structure` object to `createStudio(structure)` to define the schema.
54
+
55
+ Use the helpers:
56
+
57
+ ```ts
58
+ import {
59
+ defineBlock,
60
+ defineField,
61
+ defineStructure,
62
+ defineBlockField
63
+ } from '@alstar/studio'
64
+ ```
65
+
66
+ ### Example: Schema Definition
67
+
68
+ ```ts
69
+ const titleField = defineField({
70
+ label: 'Title',
71
+ type: 'text' | 'image' | 'markdown' | 'slug',
72
+ description: 'Page title'
73
+ })
74
+
75
+ const pageBuilder = defineBlockField({
76
+ label: 'Sections',
77
+ type: 'blocks',
78
+ children: {
79
+ hero: defineBlock({
80
+ label: 'Hero',
81
+ type: 'hero',
82
+ fields: { /* fields */ },
83
+ }),
84
+ gallery: defineBlock({
85
+ label: 'Gallery',
86
+ type: 'gallery',
87
+ fields: { /* fields */ },
88
+ }),
89
+ },
90
+ })
91
+
92
+ const entryBlock = defineBlock({
93
+ label: 'Entry',
94
+ type: 'entry',
95
+ fields: {
96
+ title: titleField,
97
+ builder: pageBuilder
98
+ }
99
+ })
100
+
101
+ export default defineStructure({
102
+ entry: entryBlock
103
+ })
104
+ ```
105
+
106
+ ### Concepts
107
+
108
+ * **Blocks** contain **fields**.
109
+ * **Block fields** (`type: 'blocks'`) can nest multiple block types under `children`.
110
+ * This enables **page builders** and reusable structures.
111
+
112
+ All content is stored in a **SQLite database** (`studio.db`) and can be queried in the templates with the `query` module.
113
+
114
+
115
+ ## Frontend
116
+
117
+ The framework encourages **server-side rendering** with Hono’s HTML helper (re-exported by the `@alstar/studio` package):
118
+
119
+ ```ts
120
+ import { defineEntry, html } from '@alstar/studio'
121
+
122
+ export default defineEntry((c) => {
123
+ const slug = c.req.param('slug')
124
+
125
+ return html`
126
+ <h1>Hello World</h1>
127
+ <p>This page is: ${slug}</p>
128
+ `
129
+ })
130
+ ```
131
+
132
+ ### Interactivity
133
+
134
+ Even though the framework allows for having any library and tool for creating client-side behavior, it's recommended to use lightweight libraries such as:
135
+
136
+ * [Datastar](https://data-star.dev/) (used internally by the Studio)
137
+ * [Alpine.js](https://alpinejs.dev/)
138
+
139
+ ## Quickstart Example Project
140
+
141
+ This example shows how to define a simple **page schema** and render it on the frontend.
142
+
143
+ ### 1. Define the CMS Schema (`./index.ts`)
144
+
145
+ ```ts
146
+ import { createStudio, defineBlock, defineField, defineStructure } from '@alstar/studio'
147
+
148
+ const page = defineBlock({
149
+ label: 'Page',
150
+ type: 'page',
151
+ fields: {
152
+ title: defineField({
153
+ label: 'Title',
154
+ type: 'text',
155
+ }),
156
+ slug: defineField({
157
+ label: 'Slug',
158
+ type: 'slug',
159
+ }),
160
+ body: defineField({
161
+ label: 'Body',
162
+ type: 'markdown',
163
+ }),
164
+ },
165
+ })
166
+
167
+ const structure = defineStructure({
168
+ page,
169
+ })
170
+
171
+ await createStudio(structure)
172
+ ```
173
+
174
+ ### 2. Create a Frontend Route (`/pages/[slug].ts`)
175
+
176
+ ```ts
177
+ import { defineEntry, html, query } from '@alstar/studio'
178
+
179
+ export default defineEntry(c) => {
180
+ const slug = c.req.param('slug')
181
+ const page = query.root({ type: 'slug', value: slug })
182
+
183
+ if (!page) return c.notFound()
184
+
185
+ return html`
186
+ <main>
187
+ <h1>${page.fields.title.value}</h1>
188
+ <article>${page.fields.body.value}</article>
189
+ </main>
190
+ `
191
+ }
192
+ ```
193
+
194
+ ### 3. Run the Project
195
+
196
+ ```sh
197
+ pnpm run dev
198
+ ```
199
+
200
+ Visit:
201
+
202
+ * **CMS admin**: `http://localhost:3000/admin`
203
+ * **Frontend page**: `http://localhost:3000/my-first-page`
204
+
205
+ Create a new page in the CMS, set its slug field to `my-first-page`, and the frontend will render it automatically.
package/schemas.ts CHANGED
@@ -1,56 +1,5 @@
1
1
  import { sql } from './utils/sql.ts'
2
2
 
3
- // export const entriesTable = {
4
- // tableName: 'entries',
5
- // columns: sql`
6
- // title TEXT not null, -- Title of the page
7
- // slug TEXT not null unique, -- URL slug for the page
8
- // meta_description TEXT -- Optional meta description for SEO
9
- // `,
10
- // }
11
-
12
- // export const fieldTable = {
13
- // tableName: 'fields',
14
- // columns: sql`
15
- // name TEXT not null, -- Name of the field (e.g., "content", "header", "image")
16
- // type TEXT not null, -- Field type (e.g., "text", "image", "video")
17
- // label TEXT not null, -- Field label (e.g., "Text", "Image", "Video")
18
- // options TEXT -- Additional options or settings (can be a JSON string if needed)
19
- // `,
20
- // }
21
-
22
- // export const entriesFieldsTable = {
23
- // tableName: 'entry_fields',
24
- // columns: sql`
25
- // entry_id INTEGER not null, -- Foreign key to pages
26
- // field_id INTEGER not null, -- Foreign key to fields
27
- // position INTEGER, -- Optional: order of the field on the page
28
- // content TEXT, -- Content of the field (e.g., text, image URL, etc.)
29
- // foreign key (entry_id) references entries (id),
30
- // foreign key (field_id) references fields (id)
31
- // `,
32
- // }
33
-
34
- // export const entryTypeTable = {
35
- // tableName: 'entry_types',
36
- // columns: sql`
37
- // name TEXT not null, -- Name of the field (e.g., "content", "header", "image")
38
- // type TEXT not null, -- Field type (e.g., "text", "image", "video")
39
- // label TEXT not null, -- Field label (e.g., "Text", "Image", "Video")
40
- // options TEXT -- Additional options or settings (can be a JSON string if needed)
41
- // `,
42
- // }
43
-
44
- // export const entryEntryTypeTable = {
45
- // tableName: 'entry_entry_types',
46
- // columns: sql`
47
- // entry_id INTEGER not null, -- Foreign key to pages
48
- // entry_type_id INTEGER not null, -- Foreign key to fields
49
- // foreign key (entry_id) references entries (id),
50
- // foreign key (entry_type_id) references entry_types (id)
51
- // `,
52
- // }
53
-
54
3
  // -- Blocks
55
4
  export const blocksTable = {
56
5
  tableName: 'blocks',
@@ -58,10 +7,22 @@ export const blocksTable = {
58
7
  name TEXT not null,
59
8
  label TEXT not null,
60
9
  type TEXT not null,
61
- sort_order INTEGER not null default 0,
62
10
  value TEXT,
63
11
  options JSON,
64
- parent_block_id INTEGER,
65
- foreign key (parent_block_id) references blocks (id)
12
+ status TEXT default 'enabled',
13
+ sort_order INTEGER not null default 0,
14
+ _depth INTEGER,
15
+ parent_id INTEGER,
16
+ foreign key (parent_id) references blocks (id)
17
+ `,
18
+ }
19
+
20
+ // -- API keys
21
+ export const apiKeysTable = {
22
+ tableName: 'api_keys',
23
+ columns: sql`
24
+ name TEXT not null,
25
+ value TEXT,
26
+ hint TEXT
66
27
  `,
67
28
  }