@createcms/core 0.1.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 +169 -0
- package/dist/ab-edge/index.cjs +214 -0
- package/dist/ab-edge/index.d.cts +121 -0
- package/dist/ab-edge/index.d.ts +121 -0
- package/dist/ab-edge/index.js +205 -0
- package/dist/bin/createcms.js +3082 -0
- package/dist/db.cjs +496 -0
- package/dist/db.d.cts +128 -0
- package/dist/db.d.ts +128 -0
- package/dist/db.js +488 -0
- package/dist/index.cjs +13789 -0
- package/dist/index.d.cts +10277 -0
- package/dist/index.d.ts +10277 -0
- package/dist/index.js +13737 -0
- package/dist/nanoid.cjs +50 -0
- package/dist/nanoid.d.cts +29 -0
- package/dist/nanoid.d.ts +29 -0
- package/dist/nanoid.js +47 -0
- package/dist/next/index.cjs +60 -0
- package/dist/next/index.d.cts +141 -0
- package/dist/next/index.d.ts +141 -0
- package/dist/next/index.js +58 -0
- package/dist/next/middleware.cjs +113 -0
- package/dist/next/middleware.d.cts +77 -0
- package/dist/next/middleware.d.ts +77 -0
- package/dist/next/middleware.js +111 -0
- package/dist/plugins/ab-test/analytics/upstash.cjs +345 -0
- package/dist/plugins/ab-test/analytics/upstash.d.cts +193 -0
- package/dist/plugins/ab-test/analytics/upstash.d.ts +193 -0
- package/dist/plugins/ab-test/analytics/upstash.js +343 -0
- package/dist/plugins/ab-test/client.cjs +686 -0
- package/dist/plugins/ab-test/client.d.cts +233 -0
- package/dist/plugins/ab-test/client.d.ts +233 -0
- package/dist/plugins/ab-test/client.js +684 -0
- package/dist/plugins/ab-test/index.cjs +3400 -0
- package/dist/plugins/ab-test/index.d.cts +1131 -0
- package/dist/plugins/ab-test/index.d.ts +1131 -0
- package/dist/plugins/ab-test/index.js +3367 -0
- package/dist/plugins/client.cjs +20 -0
- package/dist/plugins/client.d.cts +3 -0
- package/dist/plugins/client.d.ts +3 -0
- package/dist/plugins/client.js +3 -0
- package/dist/plugins/consent/client.cjs +315 -0
- package/dist/plugins/consent/client.d.cts +145 -0
- package/dist/plugins/consent/client.d.ts +145 -0
- package/dist/plugins/consent/client.js +313 -0
- package/dist/plugins/consent/index.cjs +267 -0
- package/dist/plugins/consent/index.d.cts +618 -0
- package/dist/plugins/consent/index.d.ts +618 -0
- package/dist/plugins/consent/index.js +258 -0
- package/dist/plugins/i18n/index.cjs +2177 -0
- package/dist/plugins/i18n/index.d.cts +562 -0
- package/dist/plugins/i18n/index.d.ts +562 -0
- package/dist/plugins/i18n/index.js +2150 -0
- package/dist/plugins/media-optimize/index.cjs +315 -0
- package/dist/plugins/media-optimize/index.d.cts +144 -0
- package/dist/plugins/media-optimize/index.d.ts +144 -0
- package/dist/plugins/media-optimize/index.js +311 -0
- package/dist/plugins/multi-tenant/index.cjs +210 -0
- package/dist/plugins/multi-tenant/index.d.cts +431 -0
- package/dist/plugins/multi-tenant/index.d.ts +431 -0
- package/dist/plugins/multi-tenant/index.js +207 -0
- package/dist/plugins/server.cjs +24 -0
- package/dist/plugins/server.d.cts +3 -0
- package/dist/plugins/server.d.ts +3 -0
- package/dist/plugins/server.js +3 -0
- package/dist/react/blocks.cjs +233 -0
- package/dist/react/blocks.d.cts +320 -0
- package/dist/react/blocks.d.ts +320 -0
- package/dist/react/blocks.js +226 -0
- package/dist/react/index.cjs +901 -0
- package/dist/react/index.d.cts +992 -0
- package/dist/react/index.d.ts +992 -0
- package/dist/react/index.js +872 -0
- package/dist/react/tracking.cjs +243 -0
- package/dist/react/tracking.d.cts +364 -0
- package/dist/react/tracking.d.ts +364 -0
- package/dist/react/tracking.js +216 -0
- package/dist/react/variant.cjs +59 -0
- package/dist/react/variant.d.cts +26 -0
- package/dist/react/variant.d.ts +26 -0
- package/dist/react/variant.js +57 -0
- package/package.json +303 -0
package/dist/db.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
type SchemaNamespace = 'cms';
|
|
2
|
+
type ColumnScalarType = 'text' | 'boolean' | 'integer' | 'timestamp' | 'jsonb' | 'tsvector';
|
|
3
|
+
type ColumnType<EnumTarget extends string = string> = ColumnScalarType | {
|
|
4
|
+
enum: EnumTarget;
|
|
5
|
+
};
|
|
6
|
+
type DefaultValue = {
|
|
7
|
+
kind: 'literal';
|
|
8
|
+
value: boolean | number | string | string[] | Record<string, unknown>;
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'sql';
|
|
11
|
+
value: string;
|
|
12
|
+
};
|
|
13
|
+
type ForeignKeyAction = 'cascade' | 'restrict' | 'no action' | 'set null' | 'set default';
|
|
14
|
+
type IndexUsing = 'btree' | 'gin';
|
|
15
|
+
type ColumnDefinition<ReferenceTarget extends string = string, EnumTarget extends string = string> = {
|
|
16
|
+
type: ColumnType<EnumTarget>;
|
|
17
|
+
columnName?: string;
|
|
18
|
+
notNull?: boolean;
|
|
19
|
+
primaryKey?: boolean;
|
|
20
|
+
unique?: boolean;
|
|
21
|
+
default?: DefaultValue;
|
|
22
|
+
defaultId?: boolean;
|
|
23
|
+
defaultIdPrefix?: string;
|
|
24
|
+
defaultNow?: boolean;
|
|
25
|
+
jsonType?: string;
|
|
26
|
+
references?: {
|
|
27
|
+
table: ReferenceTarget;
|
|
28
|
+
column: string;
|
|
29
|
+
onDelete?: ForeignKeyAction;
|
|
30
|
+
onUpdate?: ForeignKeyAction;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
type TableColumns<ReferenceTarget extends string = string, EnumTarget extends string = string> = Record<string, ColumnDefinition<ReferenceTarget, EnumTarget>>;
|
|
34
|
+
type IndexDefinition<ColumnName extends string> = {
|
|
35
|
+
columns: readonly ColumnName[];
|
|
36
|
+
unique?: boolean;
|
|
37
|
+
using?: IndexUsing;
|
|
38
|
+
where?: string;
|
|
39
|
+
};
|
|
40
|
+
type CompositePrimaryKey<ColumnName extends string> = {
|
|
41
|
+
columns: readonly ColumnName[];
|
|
42
|
+
};
|
|
43
|
+
type TableLevelForeignKey<ColumnName extends string = string> = {
|
|
44
|
+
columns: readonly ColumnName[];
|
|
45
|
+
foreignTable: string;
|
|
46
|
+
foreignColumns: readonly string[];
|
|
47
|
+
name?: string;
|
|
48
|
+
onDelete?: ForeignKeyAction;
|
|
49
|
+
onUpdate?: ForeignKeyAction;
|
|
50
|
+
};
|
|
51
|
+
type TableDefinition<Columns extends TableColumns = TableColumns, ReferenceTarget extends string = string, EnumTarget extends string = string> = {
|
|
52
|
+
tableName?: string;
|
|
53
|
+
indexPrefix?: string;
|
|
54
|
+
columns: Columns;
|
|
55
|
+
indexes?: Record<string, IndexDefinition<Extract<keyof Columns, string>>>;
|
|
56
|
+
compositePrimaryKey?: CompositePrimaryKey<Extract<keyof Columns, string>>;
|
|
57
|
+
foreignKeys?: TableLevelForeignKey<Extract<keyof Columns, string>>[];
|
|
58
|
+
};
|
|
59
|
+
type TableExtension<ExistingColumns extends TableColumns, AddedColumns extends TableColumns> = {
|
|
60
|
+
columns: AddedColumns;
|
|
61
|
+
indexes?: Record<string, IndexDefinition<Extract<keyof ExistingColumns | keyof AddedColumns, string>>>;
|
|
62
|
+
};
|
|
63
|
+
type EnumDefinition = {
|
|
64
|
+
values: readonly string[];
|
|
65
|
+
enumName?: string;
|
|
66
|
+
};
|
|
67
|
+
type EnumMap = Record<string, EnumDefinition>;
|
|
68
|
+
type TableMap = Record<string, TableDefinition>;
|
|
69
|
+
type TableColumnsOf<TTable> = TTable extends TableDefinition<infer TColumns, any, any> ? TColumns : never;
|
|
70
|
+
type ExtensionMap<ExistingTables extends TableMap, OwnTables extends TableMap = {}> = Partial<{
|
|
71
|
+
[K in keyof ExistingTables | keyof OwnTables]: TableExtension<TableColumnsOf<(ExistingTables & OwnTables)[K]>, TableColumns>;
|
|
72
|
+
}>;
|
|
73
|
+
type SchemaModule<_Namespace extends SchemaNamespace = SchemaNamespace, Tables extends TableMap = {}, Enums extends EnumMap = {}, Extensions = {}> = {
|
|
74
|
+
enums?: Enums;
|
|
75
|
+
tables?: Tables;
|
|
76
|
+
extend?: Extensions;
|
|
77
|
+
};
|
|
78
|
+
type ResolvedEnum = EnumDefinition & {
|
|
79
|
+
key: string;
|
|
80
|
+
dbName: string;
|
|
81
|
+
};
|
|
82
|
+
type ResolvedTable = {
|
|
83
|
+
key: string;
|
|
84
|
+
dbName: string;
|
|
85
|
+
indexPrefix?: string;
|
|
86
|
+
columns: TableColumns;
|
|
87
|
+
indexes: Record<string, IndexDefinition<string>>;
|
|
88
|
+
compositePrimaryKey?: CompositePrimaryKey<string>;
|
|
89
|
+
foreignKeys?: TableLevelForeignKey[];
|
|
90
|
+
};
|
|
91
|
+
type MergedSchema = {
|
|
92
|
+
enums: Record<string, ResolvedEnum>;
|
|
93
|
+
tables: Record<string, ResolvedTable>;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
type SchemaInput<Tables extends TableMap, Enums extends EnumMap> = {
|
|
97
|
+
enums?: Enums;
|
|
98
|
+
tables: Tables;
|
|
99
|
+
};
|
|
100
|
+
declare function defineColumns<TColumns extends TableColumns>(columns: TColumns): TColumns;
|
|
101
|
+
declare function defineTable<TTable extends TableDefinition>(table: TTable): TTable;
|
|
102
|
+
declare function defineCoreSchema<Tables extends TableMap, Enums extends EnumMap = {}>(schema: SchemaInput<Tables, Enums>): SchemaModule<'cms', Tables, Enums> & {
|
|
103
|
+
tables: Tables;
|
|
104
|
+
};
|
|
105
|
+
declare function definePluginSchema<ExistingTables extends TableMap, _ExistingEnums extends EnumMap = {}, Tables extends TableMap = {}, Enums extends EnumMap = {}, Extensions extends ExtensionMap<ExistingTables, Tables> = {}>(schema: {
|
|
106
|
+
enums?: Enums;
|
|
107
|
+
tables?: Tables;
|
|
108
|
+
extend?: Extensions;
|
|
109
|
+
}): SchemaModule<'cms', Tables, Enums, Extensions>;
|
|
110
|
+
|
|
111
|
+
type SchemaSource = {
|
|
112
|
+
name: string;
|
|
113
|
+
schema: SchemaModule;
|
|
114
|
+
};
|
|
115
|
+
declare function toSnakeCase(value: string): string;
|
|
116
|
+
declare function mergeSchemaSources(sources: SchemaSource[]): MergedSchema;
|
|
117
|
+
|
|
118
|
+
type EmitOptions = {
|
|
119
|
+
/**
|
|
120
|
+
* Override the ID generation import statement.
|
|
121
|
+
* Defaults to `import { newId } from '@createcms/core/nanoid';`
|
|
122
|
+
*/
|
|
123
|
+
nanoidImport?: string;
|
|
124
|
+
};
|
|
125
|
+
declare function emitDrizzleSchema(schema: MergedSchema, options?: EmitOptions): string;
|
|
126
|
+
|
|
127
|
+
export { defineColumns, defineCoreSchema, definePluginSchema, defineTable, emitDrizzleSchema, mergeSchemaSources, toSnakeCase };
|
|
128
|
+
export type { ColumnDefinition, ColumnScalarType, ColumnType, CompositePrimaryKey, DefaultValue, EmitOptions, EnumDefinition, EnumMap, ExtensionMap, ForeignKeyAction, IndexDefinition, MergedSchema, ResolvedEnum, ResolvedTable, SchemaModule, SchemaSource, TableColumns, TableDefinition, TableExtension, TableLevelForeignKey, TableMap };
|
package/dist/db.js
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
function defineColumns(columns) {
|
|
2
|
+
return columns;
|
|
3
|
+
}
|
|
4
|
+
function defineTable(table) {
|
|
5
|
+
return table;
|
|
6
|
+
}
|
|
7
|
+
function defineCoreSchema(schema) {
|
|
8
|
+
return {
|
|
9
|
+
...schema
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function definePluginSchema(schema) {
|
|
13
|
+
return {
|
|
14
|
+
...schema
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function toSnakeCase(value) {
|
|
19
|
+
return value.replace(/([a-z0-9])([A-Z])/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase();
|
|
20
|
+
}
|
|
21
|
+
function cloneColumns(columns) {
|
|
22
|
+
return Object.fromEntries(Object.entries(columns).map(([key, value])=>[
|
|
23
|
+
key,
|
|
24
|
+
{
|
|
25
|
+
...value
|
|
26
|
+
}
|
|
27
|
+
]));
|
|
28
|
+
}
|
|
29
|
+
function cloneIndexes(indexes) {
|
|
30
|
+
return Object.fromEntries(Object.entries(indexes).map(([key, value])=>[
|
|
31
|
+
key,
|
|
32
|
+
{
|
|
33
|
+
...value,
|
|
34
|
+
columns: [
|
|
35
|
+
...value.columns
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
]));
|
|
39
|
+
}
|
|
40
|
+
function cloneEnum(enumDef) {
|
|
41
|
+
return {
|
|
42
|
+
...enumDef,
|
|
43
|
+
key: '',
|
|
44
|
+
dbName: '',
|
|
45
|
+
values: [
|
|
46
|
+
...enumDef.values
|
|
47
|
+
]
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function mergeSchemaSources(sources) {
|
|
51
|
+
const enums = {};
|
|
52
|
+
const tables = {};
|
|
53
|
+
for (const source of sources){
|
|
54
|
+
const sourceEnums = source.schema.enums ?? {};
|
|
55
|
+
for (const [enumKey, enumDef] of Object.entries(sourceEnums)){
|
|
56
|
+
if (enums[enumKey]) {
|
|
57
|
+
throw new Error(`Duplicate enum "${enumKey}" declared by "${source.name}".`);
|
|
58
|
+
}
|
|
59
|
+
const resolved = cloneEnum(enumDef);
|
|
60
|
+
resolved.key = enumKey;
|
|
61
|
+
resolved.dbName = enumDef.enumName ?? toSnakeCase(enumKey);
|
|
62
|
+
enums[enumKey] = resolved;
|
|
63
|
+
}
|
|
64
|
+
const sourceTables = source.schema.tables ?? {};
|
|
65
|
+
for (const [tableKey, tableDef] of Object.entries(sourceTables)){
|
|
66
|
+
if (tables[tableKey]) {
|
|
67
|
+
throw new Error(`Duplicate table "${tableKey}" declared by "${source.name}".`);
|
|
68
|
+
}
|
|
69
|
+
const resolved = {
|
|
70
|
+
key: tableKey,
|
|
71
|
+
dbName: tableDef.tableName ?? toSnakeCase(tableKey),
|
|
72
|
+
indexPrefix: tableDef.indexPrefix,
|
|
73
|
+
columns: cloneColumns(tableDef.columns),
|
|
74
|
+
indexes: cloneIndexes(Object.fromEntries(Object.entries(tableDef.indexes ?? {}).map(([indexKey, indexDef])=>[
|
|
75
|
+
indexKey,
|
|
76
|
+
{
|
|
77
|
+
...indexDef,
|
|
78
|
+
columns: [
|
|
79
|
+
...indexDef.columns
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
]))),
|
|
83
|
+
compositePrimaryKey: tableDef.compositePrimaryKey ? {
|
|
84
|
+
columns: [
|
|
85
|
+
...tableDef.compositePrimaryKey.columns
|
|
86
|
+
]
|
|
87
|
+
} : undefined,
|
|
88
|
+
foreignKeys: tableDef.foreignKeys ? tableDef.foreignKeys.map((fk)=>({
|
|
89
|
+
...fk,
|
|
90
|
+
columns: [
|
|
91
|
+
...fk.columns
|
|
92
|
+
],
|
|
93
|
+
foreignColumns: [
|
|
94
|
+
...fk.foreignColumns
|
|
95
|
+
]
|
|
96
|
+
})) : undefined
|
|
97
|
+
};
|
|
98
|
+
tables[tableKey] = resolved;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Phase 2: Apply extensions
|
|
102
|
+
for (const source of sources){
|
|
103
|
+
const sourceExtensions = source.schema.extend ?? {};
|
|
104
|
+
for (const [targetTableKey, extension] of Object.entries(sourceExtensions)){
|
|
105
|
+
const targetTable = tables[targetTableKey];
|
|
106
|
+
if (!targetTable) {
|
|
107
|
+
throw new Error(`Schema source "${source.name}" extends unknown table "${targetTableKey}".`);
|
|
108
|
+
}
|
|
109
|
+
for (const [columnKey, columnDef] of Object.entries(extension.columns)){
|
|
110
|
+
if (targetTable.columns[columnKey]) {
|
|
111
|
+
throw new Error(`Schema source "${source.name}" tried to add duplicate column "${columnKey}" to "${targetTableKey}".`);
|
|
112
|
+
}
|
|
113
|
+
targetTable.columns[columnKey] = {
|
|
114
|
+
...columnDef
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
for (const [indexKey, indexDef] of Object.entries(extension.indexes ?? {})){
|
|
118
|
+
if (targetTable.indexes[indexKey]) {
|
|
119
|
+
throw new Error(`Schema source "${source.name}" tried to add duplicate index "${indexKey}" to "${targetTableKey}".`);
|
|
120
|
+
}
|
|
121
|
+
targetTable.indexes[indexKey] = {
|
|
122
|
+
...indexDef,
|
|
123
|
+
columns: [
|
|
124
|
+
...indexDef.columns
|
|
125
|
+
]
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Phase 3: Validation
|
|
131
|
+
const enumDbNames = new Set();
|
|
132
|
+
for (const enumDef of Object.values(enums)){
|
|
133
|
+
if (enumDbNames.has(enumDef.dbName)) {
|
|
134
|
+
throw new Error(`Duplicate enum database name "${enumDef.dbName}".`);
|
|
135
|
+
}
|
|
136
|
+
enumDbNames.add(enumDef.dbName);
|
|
137
|
+
}
|
|
138
|
+
const tableDbNames = new Set();
|
|
139
|
+
for (const table of Object.values(tables)){
|
|
140
|
+
if (tableDbNames.has(table.dbName)) {
|
|
141
|
+
throw new Error(`Duplicate table database name "${table.dbName}".`);
|
|
142
|
+
}
|
|
143
|
+
tableDbNames.add(table.dbName);
|
|
144
|
+
for (const [columnKey, columnDef] of Object.entries(table.columns)){
|
|
145
|
+
if (typeof columnDef.type === 'object') {
|
|
146
|
+
const enumName = columnDef.type.enum;
|
|
147
|
+
if (!enums[enumName]) {
|
|
148
|
+
throw new Error(`Column "${table.key}.${columnKey}" references unknown enum "${enumName}".`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (columnDef.references) {
|
|
152
|
+
const referencedTable = tables[columnDef.references.table];
|
|
153
|
+
if (!referencedTable) {
|
|
154
|
+
throw new Error(`Column "${table.key}.${columnKey}" references unknown table "${columnDef.references.table}".`);
|
|
155
|
+
}
|
|
156
|
+
if (!referencedTable.columns[columnDef.references.column]) {
|
|
157
|
+
throw new Error(`Column "${table.key}.${columnKey}" references unknown column "${columnDef.references.table}.${columnDef.references.column}".`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
for (const [indexKey, indexDef] of Object.entries(table.indexes)){
|
|
162
|
+
for (const columnName of indexDef.columns){
|
|
163
|
+
if (!table.columns[columnName]) {
|
|
164
|
+
throw new Error(`Index "${table.key}.${indexKey}" references unknown column "${columnName}".`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (table.foreignKeys) {
|
|
169
|
+
for (const fk of table.foreignKeys){
|
|
170
|
+
for (const col of fk.columns){
|
|
171
|
+
if (!table.columns[col]) {
|
|
172
|
+
throw new Error(`Foreign key in "${table.key}" references unknown local column "${col}".`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const refTable = tables[fk.foreignTable];
|
|
176
|
+
if (!refTable) {
|
|
177
|
+
throw new Error(`Foreign key in "${table.key}" references unknown table "${fk.foreignTable}".`);
|
|
178
|
+
}
|
|
179
|
+
for (const col of fk.foreignColumns){
|
|
180
|
+
if (!refTable.columns[col]) {
|
|
181
|
+
throw new Error(`Foreign key in "${table.key}" references unknown column "${fk.foreignTable}.${col}".`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
enums,
|
|
189
|
+
tables
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function quote(value) {
|
|
194
|
+
return JSON.stringify(value);
|
|
195
|
+
}
|
|
196
|
+
function toPascalCase(value) {
|
|
197
|
+
return value.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[_\-\s]+/g, ' ').split(' ').filter(Boolean).map((part)=>part.charAt(0).toUpperCase() + part.slice(1)).join('');
|
|
198
|
+
}
|
|
199
|
+
function singularize(value) {
|
|
200
|
+
if (value.endsWith('ies')) {
|
|
201
|
+
return `${value.slice(0, -3)}y`;
|
|
202
|
+
}
|
|
203
|
+
if (value.endsWith('ches') || value.endsWith('shes') || value.endsWith('xes') || value.endsWith('zes')) {
|
|
204
|
+
return value.slice(0, -2);
|
|
205
|
+
}
|
|
206
|
+
if (value.endsWith('s') && !value.endsWith('ss')) {
|
|
207
|
+
return value.slice(0, -1);
|
|
208
|
+
}
|
|
209
|
+
return value;
|
|
210
|
+
}
|
|
211
|
+
function emitLiteral(value) {
|
|
212
|
+
return JSON.stringify(value.value);
|
|
213
|
+
}
|
|
214
|
+
function emitDefault(defaultValue) {
|
|
215
|
+
switch(defaultValue.kind){
|
|
216
|
+
case 'literal':
|
|
217
|
+
return {
|
|
218
|
+
code: `.default(${emitLiteral(defaultValue)})`,
|
|
219
|
+
needsSql: false
|
|
220
|
+
};
|
|
221
|
+
case 'sql':
|
|
222
|
+
return {
|
|
223
|
+
code: `.default(sql.raw(${quote(defaultValue.value)}))`,
|
|
224
|
+
needsSql: true
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function emitEnum(enumDef) {
|
|
229
|
+
const values = `[${enumDef.values.map(quote).join(', ')}]`;
|
|
230
|
+
return `export const ${enumDef.key}Enum = cms.enum(${quote(enumDef.dbName)}, ${values});`;
|
|
231
|
+
}
|
|
232
|
+
function emitColumn(table, columnKey, columnDef) {
|
|
233
|
+
const columnName = columnDef.columnName ?? toSnakeCase(columnKey);
|
|
234
|
+
let builder = '';
|
|
235
|
+
let needsTsvector = false;
|
|
236
|
+
if (typeof columnDef.type === 'string') {
|
|
237
|
+
if (columnDef.type === 'tsvector') {
|
|
238
|
+
builder = `tsvectorColumn(${quote(columnName)})`;
|
|
239
|
+
needsTsvector = true;
|
|
240
|
+
} else {
|
|
241
|
+
builder = `${columnDef.type}(${quote(columnName)})`;
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
builder = `${columnDef.type.enum}Enum(${quote(columnName)})`;
|
|
245
|
+
}
|
|
246
|
+
let needsSql = false;
|
|
247
|
+
let needsAnyPgColumn = false;
|
|
248
|
+
if (columnDef.jsonType && typeof columnDef.type === 'string' && columnDef.type === 'jsonb') {
|
|
249
|
+
builder += `.$type<${columnDef.jsonType}>()`;
|
|
250
|
+
}
|
|
251
|
+
if (columnDef.primaryKey) {
|
|
252
|
+
builder += '.primaryKey()';
|
|
253
|
+
}
|
|
254
|
+
if (columnDef.notNull) {
|
|
255
|
+
builder += '.notNull()';
|
|
256
|
+
}
|
|
257
|
+
if (columnDef.unique) {
|
|
258
|
+
builder += '.unique()';
|
|
259
|
+
}
|
|
260
|
+
if (columnDef.defaultNow) {
|
|
261
|
+
builder += '.defaultNow()';
|
|
262
|
+
}
|
|
263
|
+
if (columnDef.defaultId) {
|
|
264
|
+
if (!columnDef.defaultIdPrefix) {
|
|
265
|
+
throw new Error(`Column "${columnKey}" has defaultId but no defaultIdPrefix`);
|
|
266
|
+
}
|
|
267
|
+
builder += `.$defaultFn(() => newId(${quote(columnDef.defaultIdPrefix)}))`;
|
|
268
|
+
}
|
|
269
|
+
if (columnDef.default) {
|
|
270
|
+
const emitted = emitDefault(columnDef.default);
|
|
271
|
+
builder += emitted.code;
|
|
272
|
+
needsSql ||= emitted.needsSql;
|
|
273
|
+
}
|
|
274
|
+
if (columnDef.references) {
|
|
275
|
+
const options = [];
|
|
276
|
+
if (columnDef.references.onDelete) {
|
|
277
|
+
options.push(`onDelete: ${quote(columnDef.references.onDelete)}`);
|
|
278
|
+
}
|
|
279
|
+
if (columnDef.references.onUpdate) {
|
|
280
|
+
options.push(`onUpdate: ${quote(columnDef.references.onUpdate)}`);
|
|
281
|
+
}
|
|
282
|
+
const suffix = options.length > 0 ? `, { ${options.join(', ')} }` : '';
|
|
283
|
+
const isSelfReference = columnDef.references.table === table.key;
|
|
284
|
+
const refCallback = isSelfReference ? `(): AnyPgColumn => ${columnDef.references.table}.${columnDef.references.column}` : `() => ${columnDef.references.table}.${columnDef.references.column}`;
|
|
285
|
+
needsAnyPgColumn ||= isSelfReference;
|
|
286
|
+
builder += `.references(${refCallback}${suffix})`;
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
code: ` ${columnKey}: ${builder},`,
|
|
290
|
+
needsSql,
|
|
291
|
+
needsAnyPgColumn,
|
|
292
|
+
needsTsvector
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function emitIndexes(table) {
|
|
296
|
+
const entries = Object.entries(table.indexes);
|
|
297
|
+
if (entries.length === 0) {
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
const prefix = table.indexPrefix ?? table.dbName;
|
|
301
|
+
return entries.map(([indexKey, indexDef])=>{
|
|
302
|
+
const factory = indexDef.unique ? 'uniqueIndex' : 'index';
|
|
303
|
+
const columns = indexDef.columns.map((column)=>`table.${column}`).join(', ');
|
|
304
|
+
let line;
|
|
305
|
+
if (indexDef.using && indexDef.using !== 'btree') {
|
|
306
|
+
line = ` ${factory}(${quote(`${prefix}_${toSnakeCase(indexKey)}`)}).using(${quote(indexDef.using)}, ${columns})`;
|
|
307
|
+
} else {
|
|
308
|
+
line = ` ${factory}(${quote(`${prefix}_${toSnakeCase(indexKey)}`)}).on(${columns})`;
|
|
309
|
+
}
|
|
310
|
+
if (indexDef.where) {
|
|
311
|
+
line += `.where(sql\`${indexDef.where}\`)`;
|
|
312
|
+
}
|
|
313
|
+
return `${line},`;
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
function emitCompositePrimaryKey(table) {
|
|
317
|
+
if (!table.compositePrimaryKey) return null;
|
|
318
|
+
const columns = table.compositePrimaryKey.columns.map((col)=>`table.${col}`).join(', ');
|
|
319
|
+
return ` primaryKey({ columns: [${columns}] }),`;
|
|
320
|
+
}
|
|
321
|
+
function emitTableLevelForeignKeys(table) {
|
|
322
|
+
if (!table.foreignKeys || table.foreignKeys.length === 0) {
|
|
323
|
+
return {
|
|
324
|
+
lines: [],
|
|
325
|
+
needsForeignKeyImport: false
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
const lines = table.foreignKeys.map((fk)=>{
|
|
329
|
+
const localCols = fk.columns.map((c)=>`table.${c}`).join(', ');
|
|
330
|
+
const isSelfReference = fk.foreignTable === table.key;
|
|
331
|
+
const foreignCols = fk.foreignColumns.map((c)=>`${isSelfReference ? 'table' : fk.foreignTable}.${c}`).join(', ');
|
|
332
|
+
const parts = [
|
|
333
|
+
` columns: [${localCols}],`,
|
|
334
|
+
` foreignColumns: [${foreignCols}],`
|
|
335
|
+
];
|
|
336
|
+
if (fk.name) {
|
|
337
|
+
parts.push(` name: ${quote(fk.name)},`);
|
|
338
|
+
}
|
|
339
|
+
let chain = '';
|
|
340
|
+
if (fk.onDelete) {
|
|
341
|
+
chain += `.onDelete(${quote(fk.onDelete)})`;
|
|
342
|
+
}
|
|
343
|
+
if (fk.onUpdate) {
|
|
344
|
+
chain += `.onUpdate(${quote(fk.onUpdate)})`;
|
|
345
|
+
}
|
|
346
|
+
return ` foreignKey({\n${parts.join('\n')}\n })${chain},`;
|
|
347
|
+
});
|
|
348
|
+
return {
|
|
349
|
+
lines,
|
|
350
|
+
needsForeignKeyImport: true
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function emitTableTypeAliases(table) {
|
|
354
|
+
const baseName = toPascalCase(singularize(table.key));
|
|
355
|
+
return `export type ${baseName} = typeof ${table.key}.$inferSelect;
|
|
356
|
+
export type New${baseName} = typeof ${table.key}.$inferInsert;`;
|
|
357
|
+
}
|
|
358
|
+
function emitTable(table) {
|
|
359
|
+
let needsSql = false;
|
|
360
|
+
let needsAnyPgColumn = false;
|
|
361
|
+
let needsForeignKeyImport = false;
|
|
362
|
+
let needsPrimaryKeyImport = false;
|
|
363
|
+
let needsTsvector = false;
|
|
364
|
+
const emittedColumns = Object.entries(table.columns).map(([columnKey, columnDef])=>{
|
|
365
|
+
const emitted = emitColumn(table, columnKey, columnDef);
|
|
366
|
+
needsSql ||= emitted.needsSql;
|
|
367
|
+
needsAnyPgColumn ||= emitted.needsAnyPgColumn;
|
|
368
|
+
needsTsvector ||= emitted.needsTsvector;
|
|
369
|
+
return emitted.code;
|
|
370
|
+
});
|
|
371
|
+
const indexes = emitIndexes(table);
|
|
372
|
+
for (const idx of Object.values(table.indexes)){
|
|
373
|
+
if (idx.where) needsSql = true;
|
|
374
|
+
}
|
|
375
|
+
const compositePK = emitCompositePrimaryKey(table);
|
|
376
|
+
if (compositePK) needsPrimaryKeyImport = true;
|
|
377
|
+
const { lines: fkLines, needsForeignKeyImport: needsFK } = emitTableLevelForeignKeys(table);
|
|
378
|
+
needsForeignKeyImport = needsFK;
|
|
379
|
+
const tableFactory = 'cms.table';
|
|
380
|
+
const callbackLines = [
|
|
381
|
+
...compositePK ? [
|
|
382
|
+
compositePK
|
|
383
|
+
] : [],
|
|
384
|
+
...fkLines,
|
|
385
|
+
...indexes
|
|
386
|
+
];
|
|
387
|
+
const callback = callbackLines.length > 0 ? `,
|
|
388
|
+
(table) => [
|
|
389
|
+
${callbackLines.join('\n')}
|
|
390
|
+
]` : '';
|
|
391
|
+
return {
|
|
392
|
+
code: `export const ${table.key} = ${tableFactory}(
|
|
393
|
+
${quote(table.dbName)},
|
|
394
|
+
{
|
|
395
|
+
${emittedColumns.join('\n')}
|
|
396
|
+
}${callback},
|
|
397
|
+
);`,
|
|
398
|
+
needsSql,
|
|
399
|
+
needsAnyPgColumn,
|
|
400
|
+
needsForeignKeyImport,
|
|
401
|
+
needsPrimaryKeyImport,
|
|
402
|
+
needsTsvector
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
function emitDrizzleSchema(schema, options) {
|
|
406
|
+
const pgCoreImports = new Set([
|
|
407
|
+
'pgSchema'
|
|
408
|
+
]);
|
|
409
|
+
let needsSql = false;
|
|
410
|
+
let needsAnyPgColumn = false;
|
|
411
|
+
let needsForeignKeyImport = false;
|
|
412
|
+
let needsPrimaryKeyImport = false;
|
|
413
|
+
let needsTsvector = false;
|
|
414
|
+
const enums = Object.values(schema.enums).sort((a, b)=>a.key.localeCompare(b.key));
|
|
415
|
+
const tables = Object.values(schema.tables).sort((a, b)=>a.key.localeCompare(b.key));
|
|
416
|
+
for (const table of tables){
|
|
417
|
+
for (const column of Object.values(table.columns)){
|
|
418
|
+
if (typeof column.type === 'string') {
|
|
419
|
+
if (column.type !== 'tsvector') {
|
|
420
|
+
pgCoreImports.add(column.type);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (column.default?.kind === 'sql') {
|
|
424
|
+
needsSql = true;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (Object.keys(table.indexes).length > 0) {
|
|
428
|
+
pgCoreImports.add('index');
|
|
429
|
+
}
|
|
430
|
+
if (Object.values(table.indexes).some((indexDef)=>indexDef.unique)) {
|
|
431
|
+
pgCoreImports.add('uniqueIndex');
|
|
432
|
+
}
|
|
433
|
+
if (Object.values(table.indexes).some((indexDef)=>indexDef.where)) {
|
|
434
|
+
needsSql = true;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
const enumBlocks = enums.map(emitEnum);
|
|
438
|
+
const tableBlocks = tables.map((table)=>{
|
|
439
|
+
const emitted = emitTable(table);
|
|
440
|
+
needsSql ||= emitted.needsSql;
|
|
441
|
+
needsAnyPgColumn ||= emitted.needsAnyPgColumn;
|
|
442
|
+
needsForeignKeyImport ||= emitted.needsForeignKeyImport;
|
|
443
|
+
needsPrimaryKeyImport ||= emitted.needsPrimaryKeyImport;
|
|
444
|
+
needsTsvector ||= emitted.needsTsvector;
|
|
445
|
+
return emitted.code;
|
|
446
|
+
});
|
|
447
|
+
const typeBlocks = tables.map(emitTableTypeAliases);
|
|
448
|
+
if (needsForeignKeyImport) pgCoreImports.add('foreignKey');
|
|
449
|
+
if (needsPrimaryKeyImport) pgCoreImports.add('primaryKey');
|
|
450
|
+
if (needsTsvector) pgCoreImports.add('customType');
|
|
451
|
+
const imports = [];
|
|
452
|
+
if (needsSql) {
|
|
453
|
+
imports.push(`import { sql } from 'drizzle-orm';`);
|
|
454
|
+
}
|
|
455
|
+
imports.push(`import { ${Array.from(pgCoreImports).sort().join(', ')} } from 'drizzle-orm/pg-core';`);
|
|
456
|
+
if (needsAnyPgColumn) {
|
|
457
|
+
imports.push(`import type { AnyPgColumn } from 'drizzle-orm/pg-core';`);
|
|
458
|
+
}
|
|
459
|
+
const needsNewId = tables.some((table)=>Object.values(table.columns).some((col)=>col.defaultId));
|
|
460
|
+
if (needsNewId) {
|
|
461
|
+
imports.push(options?.nanoidImport ?? `import { newId } from '@createcms/core/nanoid';`);
|
|
462
|
+
}
|
|
463
|
+
const tsvectorDef = needsTsvector ? `\nconst tsvectorColumn = customType<{ data: string }>({
|
|
464
|
+
dataType() { return 'tsvector'; },
|
|
465
|
+
});\n` : '';
|
|
466
|
+
const schemaExport = `export const schema = {
|
|
467
|
+
${tables.map((table)=>` ${table.key},`).join('\n')}
|
|
468
|
+
${enums.map((enumDef)=>` ${enumDef.key}Enum,`).join('\n')}
|
|
469
|
+
};`;
|
|
470
|
+
return `/* eslint-disable */
|
|
471
|
+
/**
|
|
472
|
+
* This file is generated by createcms.
|
|
473
|
+
* Do not edit manually.
|
|
474
|
+
*/
|
|
475
|
+
|
|
476
|
+
${imports.join('\n')}
|
|
477
|
+
|
|
478
|
+
export const cms = pgSchema('cms');
|
|
479
|
+
${tsvectorDef}${enumBlocks.join('\n\n')}
|
|
480
|
+
${enumBlocks.length > 0 && tableBlocks.length > 0 ? '\n' : ''}${tableBlocks.join('\n\n')}
|
|
481
|
+
|
|
482
|
+
${schemaExport}
|
|
483
|
+
|
|
484
|
+
${typeBlocks.join('\n\n')}
|
|
485
|
+
`;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export { defineColumns, defineCoreSchema, definePluginSchema, defineTable, emitDrizzleSchema, mergeSchemaSources, toSnakeCase };
|