@bobbykim/manguito-cms-cli 0.2.2 → 0.3.0
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/dist/index.js +365 -95
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -7530,13 +7530,13 @@ var require_bcrypt = __commonJS({
|
|
|
7530
7530
|
throw Error("Illegal callback: " + typeof callback);
|
|
7531
7531
|
_async(callback);
|
|
7532
7532
|
} else
|
|
7533
|
-
return new Promise(function(
|
|
7533
|
+
return new Promise(function(resolve10, reject) {
|
|
7534
7534
|
_async(function(err, res) {
|
|
7535
7535
|
if (err) {
|
|
7536
7536
|
reject(err);
|
|
7537
7537
|
return;
|
|
7538
7538
|
}
|
|
7539
|
-
|
|
7539
|
+
resolve10(res);
|
|
7540
7540
|
});
|
|
7541
7541
|
});
|
|
7542
7542
|
};
|
|
@@ -7565,13 +7565,13 @@ var require_bcrypt = __commonJS({
|
|
|
7565
7565
|
throw Error("Illegal callback: " + typeof callback);
|
|
7566
7566
|
_async(callback);
|
|
7567
7567
|
} else
|
|
7568
|
-
return new Promise(function(
|
|
7568
|
+
return new Promise(function(resolve10, reject) {
|
|
7569
7569
|
_async(function(err, res) {
|
|
7570
7570
|
if (err) {
|
|
7571
7571
|
reject(err);
|
|
7572
7572
|
return;
|
|
7573
7573
|
}
|
|
7574
|
-
|
|
7574
|
+
resolve10(res);
|
|
7575
7575
|
});
|
|
7576
7576
|
});
|
|
7577
7577
|
};
|
|
@@ -7616,13 +7616,13 @@ var require_bcrypt = __commonJS({
|
|
|
7616
7616
|
throw Error("Illegal callback: " + typeof callback);
|
|
7617
7617
|
_async(callback);
|
|
7618
7618
|
} else
|
|
7619
|
-
return new Promise(function(
|
|
7619
|
+
return new Promise(function(resolve10, reject) {
|
|
7620
7620
|
_async(function(err, res) {
|
|
7621
7621
|
if (err) {
|
|
7622
7622
|
reject(err);
|
|
7623
7623
|
return;
|
|
7624
7624
|
}
|
|
7625
|
-
|
|
7625
|
+
resolve10(res);
|
|
7626
7626
|
});
|
|
7627
7627
|
});
|
|
7628
7628
|
};
|
|
@@ -12343,7 +12343,7 @@ var require_dist2 = __commonJS({
|
|
|
12343
12343
|
function parse4(stream, callback) {
|
|
12344
12344
|
const parser = new parser_1.Parser();
|
|
12345
12345
|
stream.on("data", (buffer) => parser.parse(buffer, callback));
|
|
12346
|
-
return new Promise((
|
|
12346
|
+
return new Promise((resolve10) => stream.on("end", () => resolve10()));
|
|
12347
12347
|
}
|
|
12348
12348
|
}
|
|
12349
12349
|
});
|
|
@@ -13112,12 +13112,12 @@ var require_client = __commonJS({
|
|
|
13112
13112
|
this._connect(callback);
|
|
13113
13113
|
return;
|
|
13114
13114
|
}
|
|
13115
|
-
return new this._Promise((
|
|
13115
|
+
return new this._Promise((resolve10, reject) => {
|
|
13116
13116
|
this._connect((error51) => {
|
|
13117
13117
|
if (error51) {
|
|
13118
13118
|
reject(error51);
|
|
13119
13119
|
} else {
|
|
13120
|
-
|
|
13120
|
+
resolve10(this);
|
|
13121
13121
|
}
|
|
13122
13122
|
});
|
|
13123
13123
|
});
|
|
@@ -13464,8 +13464,8 @@ var require_client = __commonJS({
|
|
|
13464
13464
|
} else {
|
|
13465
13465
|
query = new Query2(config3, values, callback);
|
|
13466
13466
|
if (!query.callback) {
|
|
13467
|
-
result = new this._Promise((
|
|
13468
|
-
query.callback = (err, res) => err ? reject(err) :
|
|
13467
|
+
result = new this._Promise((resolve10, reject) => {
|
|
13468
|
+
query.callback = (err, res) => err ? reject(err) : resolve10(res);
|
|
13469
13469
|
}).catch((err) => {
|
|
13470
13470
|
Error.captureStackTrace(err);
|
|
13471
13471
|
throw err;
|
|
@@ -13549,8 +13549,8 @@ var require_client = __commonJS({
|
|
|
13549
13549
|
if (cb) {
|
|
13550
13550
|
this.connection.once("end", cb);
|
|
13551
13551
|
} else {
|
|
13552
|
-
return new this._Promise((
|
|
13553
|
-
this.connection.once("end",
|
|
13552
|
+
return new this._Promise((resolve10) => {
|
|
13553
|
+
this.connection.once("end", resolve10);
|
|
13554
13554
|
});
|
|
13555
13555
|
}
|
|
13556
13556
|
}
|
|
@@ -13600,8 +13600,8 @@ var require_pg_pool = __commonJS({
|
|
|
13600
13600
|
const cb = function(err, client) {
|
|
13601
13601
|
err ? rej(err) : res(client);
|
|
13602
13602
|
};
|
|
13603
|
-
const result = new Promise2(function(
|
|
13604
|
-
res =
|
|
13603
|
+
const result = new Promise2(function(resolve10, reject) {
|
|
13604
|
+
res = resolve10;
|
|
13605
13605
|
rej = reject;
|
|
13606
13606
|
}).catch((err) => {
|
|
13607
13607
|
Error.captureStackTrace(err);
|
|
@@ -13662,7 +13662,7 @@ var require_pg_pool = __commonJS({
|
|
|
13662
13662
|
if (typeof Promise2.try === "function") {
|
|
13663
13663
|
return Promise2.try(f);
|
|
13664
13664
|
}
|
|
13665
|
-
return new Promise2((
|
|
13665
|
+
return new Promise2((resolve10) => resolve10(f()));
|
|
13666
13666
|
}
|
|
13667
13667
|
_isFull() {
|
|
13668
13668
|
return this._clients.length >= this.options.max;
|
|
@@ -14056,8 +14056,8 @@ var require_query2 = __commonJS({
|
|
|
14056
14056
|
NativeQuery.prototype._getPromise = function() {
|
|
14057
14057
|
if (this._promise) return this._promise;
|
|
14058
14058
|
this._promise = new Promise(
|
|
14059
|
-
function(
|
|
14060
|
-
this._once("end",
|
|
14059
|
+
function(resolve10, reject) {
|
|
14060
|
+
this._once("end", resolve10);
|
|
14061
14061
|
this._once("error", reject);
|
|
14062
14062
|
}.bind(this)
|
|
14063
14063
|
);
|
|
@@ -14236,12 +14236,12 @@ var require_client2 = __commonJS({
|
|
|
14236
14236
|
this._connect(callback);
|
|
14237
14237
|
return;
|
|
14238
14238
|
}
|
|
14239
|
-
return new this._Promise((
|
|
14239
|
+
return new this._Promise((resolve10, reject) => {
|
|
14240
14240
|
this._connect((error51) => {
|
|
14241
14241
|
if (error51) {
|
|
14242
14242
|
reject(error51);
|
|
14243
14243
|
} else {
|
|
14244
|
-
|
|
14244
|
+
resolve10(this);
|
|
14245
14245
|
}
|
|
14246
14246
|
});
|
|
14247
14247
|
});
|
|
@@ -14265,8 +14265,8 @@ var require_client2 = __commonJS({
|
|
|
14265
14265
|
query = new NativeQuery(config3, values, callback);
|
|
14266
14266
|
if (!query.callback) {
|
|
14267
14267
|
let resolveOut, rejectOut;
|
|
14268
|
-
result = new this._Promise((
|
|
14269
|
-
resolveOut =
|
|
14268
|
+
result = new this._Promise((resolve10, reject) => {
|
|
14269
|
+
resolveOut = resolve10;
|
|
14270
14270
|
rejectOut = reject;
|
|
14271
14271
|
}).catch((err) => {
|
|
14272
14272
|
Error.captureStackTrace(err);
|
|
@@ -14329,8 +14329,8 @@ var require_client2 = __commonJS({
|
|
|
14329
14329
|
}
|
|
14330
14330
|
let result;
|
|
14331
14331
|
if (!cb) {
|
|
14332
|
-
result = new this._Promise(function(
|
|
14333
|
-
cb = (err) => err ? reject(err) :
|
|
14332
|
+
result = new this._Promise(function(resolve10, reject) {
|
|
14333
|
+
cb = (err) => err ? reject(err) : resolve10();
|
|
14334
14334
|
});
|
|
14335
14335
|
}
|
|
14336
14336
|
this.native.end(function() {
|
|
@@ -14478,7 +14478,7 @@ import { Command } from "commander";
|
|
|
14478
14478
|
// src/commands/build.ts
|
|
14479
14479
|
init_esm_shims();
|
|
14480
14480
|
import { mkdirSync, statSync as statSync2, existsSync as existsSync3, readdirSync as readdirSync2, writeFileSync } from "fs";
|
|
14481
|
-
import { resolve as resolve3, join as
|
|
14481
|
+
import { resolve as resolve3, join as join7, dirname } from "path";
|
|
14482
14482
|
import { createRequire } from "module";
|
|
14483
14483
|
import { build as viteBuild } from "vite";
|
|
14484
14484
|
import { build as tsupBuild } from "tsup";
|
|
@@ -29155,6 +29155,10 @@ var RawBooleanFieldSchema = RawFieldBase.extend({
|
|
|
29155
29155
|
var RawDateFieldSchema = RawFieldBase.extend({
|
|
29156
29156
|
type: external_exports.literal("date")
|
|
29157
29157
|
});
|
|
29158
|
+
var RawProgrammaticFieldSchema = RawFieldBase.extend({
|
|
29159
|
+
type: external_exports.literal("programmatic"),
|
|
29160
|
+
required: external_exports.boolean().optional()
|
|
29161
|
+
});
|
|
29158
29162
|
var RawImageFieldSchema = RawFieldBase.extend({
|
|
29159
29163
|
type: external_exports.literal("image"),
|
|
29160
29164
|
max_size: maxSizeString.optional(),
|
|
@@ -29201,7 +29205,8 @@ var RawNonEnumFieldSchema = external_exports.discriminatedUnion("type", [
|
|
|
29201
29205
|
RawVideoFieldSchema,
|
|
29202
29206
|
RawFileFieldSchema,
|
|
29203
29207
|
RawParagraphFieldSchema,
|
|
29204
|
-
RawReferenceFieldSchema
|
|
29208
|
+
RawReferenceFieldSchema,
|
|
29209
|
+
RawProgrammaticFieldSchema
|
|
29205
29210
|
]);
|
|
29206
29211
|
var RawFieldSchema = external_exports.union([RawNonEnumFieldSchema, RawEnumFieldSchema]);
|
|
29207
29212
|
var RawTabSchema = external_exports.object({
|
|
@@ -29786,7 +29791,14 @@ var fieldTypeRegistry = {
|
|
|
29786
29791
|
db_column,
|
|
29787
29792
|
ui_component: { component: "typeahead-select", ref: raw2.target, rel: raw2.rel }
|
|
29788
29793
|
};
|
|
29789
|
-
}
|
|
29794
|
+
},
|
|
29795
|
+
// ── Programmatic — computed at read time, no column ─────────────────────────
|
|
29796
|
+
// Inert marker: the value is produced by a user resolver in the api layer.
|
|
29797
|
+
programmatic: () => ({
|
|
29798
|
+
validation: { required: false },
|
|
29799
|
+
db_column: null,
|
|
29800
|
+
ui_component: { component: "computed-display" }
|
|
29801
|
+
})
|
|
29790
29802
|
};
|
|
29791
29803
|
var CONTENT_SYSTEM_FIELDS = [
|
|
29792
29804
|
{ name: "id", db_type: "uuid", primary_key: true, default: "gen_random_uuid()", nullable: false },
|
|
@@ -29867,8 +29879,9 @@ function buildParsedField(rawField, order, sourceFile, ownerTableName) {
|
|
|
29867
29879
|
}]
|
|
29868
29880
|
};
|
|
29869
29881
|
}
|
|
29870
|
-
const { name, label
|
|
29882
|
+
const { name, label } = rawField;
|
|
29871
29883
|
const { validation, db_column, ui_component } = build(rawField, { ownerTableName });
|
|
29884
|
+
const required2 = validation.required;
|
|
29872
29885
|
return {
|
|
29873
29886
|
ok: true,
|
|
29874
29887
|
value: {
|
|
@@ -31220,7 +31233,7 @@ function sql(strings, ...params) {
|
|
|
31220
31233
|
return new SQL([new StringChunk(str2)]);
|
|
31221
31234
|
}
|
|
31222
31235
|
sql2.raw = raw2;
|
|
31223
|
-
function
|
|
31236
|
+
function join13(chunks, separator) {
|
|
31224
31237
|
const result = [];
|
|
31225
31238
|
for (const [i, chunk] of chunks.entries()) {
|
|
31226
31239
|
if (i > 0 && separator !== void 0) {
|
|
@@ -31230,7 +31243,7 @@ function sql(strings, ...params) {
|
|
|
31230
31243
|
}
|
|
31231
31244
|
return new SQL(result);
|
|
31232
31245
|
}
|
|
31233
|
-
sql2.join =
|
|
31246
|
+
sql2.join = join13;
|
|
31234
31247
|
function identifier(value) {
|
|
31235
31248
|
return new Name(value);
|
|
31236
31249
|
}
|
|
@@ -34912,7 +34925,7 @@ var PgSelectQueryBuilderBase = class extends TypedQueryBuilder {
|
|
|
34912
34925
|
const baseTableName = this.tableName;
|
|
34913
34926
|
const tableName = getTableLikeName(table);
|
|
34914
34927
|
for (const item of extractUsedTable(table)) this.usedTables.add(item);
|
|
34915
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
34928
|
+
if (typeof tableName === "string" && this.config.joins?.some((join13) => join13.alias === tableName)) {
|
|
34916
34929
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
34917
34930
|
}
|
|
34918
34931
|
if (!this.isPartialSelect) {
|
|
@@ -36136,7 +36149,7 @@ var PgUpdateBase = class extends QueryPromise {
|
|
|
36136
36149
|
createJoin(joinType) {
|
|
36137
36150
|
return (table, on) => {
|
|
36138
36151
|
const tableName = getTableLikeName(table);
|
|
36139
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
36152
|
+
if (typeof tableName === "string" && this.config.joins.some((join13) => join13.alias === tableName)) {
|
|
36140
36153
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
36141
36154
|
}
|
|
36142
36155
|
if (typeof on === "function") {
|
|
@@ -36232,10 +36245,10 @@ var PgUpdateBase = class extends QueryPromise {
|
|
|
36232
36245
|
const fromFields = this.getTableLikeFields(this.config.from);
|
|
36233
36246
|
fields[tableName] = fromFields;
|
|
36234
36247
|
}
|
|
36235
|
-
for (const
|
|
36236
|
-
const tableName2 = getTableLikeName(
|
|
36237
|
-
if (typeof tableName2 === "string" && !is(
|
|
36238
|
-
const fromFields = this.getTableLikeFields(
|
|
36248
|
+
for (const join13 of this.config.joins) {
|
|
36249
|
+
const tableName2 = getTableLikeName(join13.table);
|
|
36250
|
+
if (typeof tableName2 === "string" && !is(join13.table, SQL)) {
|
|
36251
|
+
const fromFields = this.getTableLikeFields(join13.table);
|
|
36239
36252
|
fields[tableName2] = fromFields;
|
|
36240
36253
|
}
|
|
36241
36254
|
}
|
|
@@ -44162,7 +44175,8 @@ var COMPONENT_NAME = {
|
|
|
44162
44175
|
"file-upload": "MediaUpload",
|
|
44163
44176
|
select: "EnumSelect",
|
|
44164
44177
|
"typeahead-select": "ReferenceSelect",
|
|
44165
|
-
"paragraph-embed": "ParagraphEmbed"
|
|
44178
|
+
"paragraph-embed": "ParagraphEmbed",
|
|
44179
|
+
"computed-display": "ComputedDisplay"
|
|
44166
44180
|
};
|
|
44167
44181
|
var COMPONENT_IMPORT = {
|
|
44168
44182
|
TextInput: `${ADMIN_PKG}/src/components/fields/TextInput.vue`,
|
|
@@ -44173,7 +44187,8 @@ var COMPONENT_IMPORT = {
|
|
|
44173
44187
|
MediaUpload: `${ADMIN_PKG}/src/components/fields/MediaUpload.vue`,
|
|
44174
44188
|
EnumSelect: `${ADMIN_PKG}/src/components/fields/EnumSelect.vue`,
|
|
44175
44189
|
ReferenceSelect: `${ADMIN_PKG}/src/components/fields/ReferenceSelect.vue`,
|
|
44176
|
-
ParagraphEmbed: `${ADMIN_PKG}/src/components/fields/ParagraphEmbed.vue
|
|
44190
|
+
ParagraphEmbed: `${ADMIN_PKG}/src/components/fields/ParagraphEmbed.vue`,
|
|
44191
|
+
ComputedDisplay: `${ADMIN_PKG}/src/components/fields/ComputedDisplay.vue`
|
|
44177
44192
|
};
|
|
44178
44193
|
function esc2(s) {
|
|
44179
44194
|
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
@@ -44220,6 +44235,9 @@ function renderField(field, indent) {
|
|
|
44220
44235
|
const compName = COMPONENT_NAME[field.ui_component.component];
|
|
44221
44236
|
const fieldObj = buildFieldObject(field);
|
|
44222
44237
|
const attr = indent + " ";
|
|
44238
|
+
if (field.ui_component.component === "computed-display") {
|
|
44239
|
+
return [`${indent}<${compName}`, `${attr}:field="${fieldObj}"`, `${indent}/>`].join("\n");
|
|
44240
|
+
}
|
|
44223
44241
|
const lines = [`${indent}<${compName}`];
|
|
44224
44242
|
lines.push(`${attr}:field="${fieldObj}"`);
|
|
44225
44243
|
if (field.ui_component.component === "paragraph-embed") {
|
|
@@ -44327,6 +44345,7 @@ async function generateServerEntries(options, targetDir) {
|
|
|
44327
44345
|
function appSetup() {
|
|
44328
44346
|
return `import config from '../../manguito.config.js'
|
|
44329
44347
|
import { schemaRegistry } from './schema-registry.js'
|
|
44348
|
+
import { programmaticResolvers } from './programmatic-registry.js'
|
|
44330
44349
|
import { createCmsApp } from '@bobbykim/manguito-cms-api'
|
|
44331
44350
|
import { createPostgresAdapter } from '@bobbykim/manguito-cms-db'
|
|
44332
44351
|
|
|
@@ -44339,6 +44358,7 @@ const { app } = createCmsApp({
|
|
|
44339
44358
|
db: dbAdapter.getDb(),
|
|
44340
44359
|
storage: config.storage,
|
|
44341
44360
|
prefix: config.api.prefix,
|
|
44361
|
+
resolvers: programmaticResolvers,
|
|
44342
44362
|
...(config.api.media ? { media: config.api.media } : {}),
|
|
44343
44363
|
...(config.api.rateLimit ? { rateLimit: config.api.rateLimit } : {}),
|
|
44344
44364
|
...(config.server.cors ? { cors: config.server.cors } : {}),
|
|
@@ -44499,6 +44519,34 @@ export default handle(app)
|
|
|
44499
44519
|
`;
|
|
44500
44520
|
}
|
|
44501
44521
|
|
|
44522
|
+
// src/codegen/programmatic-registry.ts
|
|
44523
|
+
init_esm_shims();
|
|
44524
|
+
import { writeFile as writeFile5 } from "fs/promises";
|
|
44525
|
+
import { join as join6, relative } from "path";
|
|
44526
|
+
function importSpecifier(targetDir, file2) {
|
|
44527
|
+
let rel = relative(targetDir, file2).replace(/\\/g, "/");
|
|
44528
|
+
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
44529
|
+
return rel.replace(/\.ts$/, ".js");
|
|
44530
|
+
}
|
|
44531
|
+
async function generateProgrammaticRegistry(files, targetDir) {
|
|
44532
|
+
const sorted = [...files].sort();
|
|
44533
|
+
const imports = sorted.map((file2, i) => `import def${i} from '${importSpecifier(targetDir, file2)}'`).join("\n");
|
|
44534
|
+
const defs = sorted.map((_f, i) => `def${i}`);
|
|
44535
|
+
const entries = defs.map((d2) => ` [\`\${${d2}.schema}::\${${d2}.field}\`, ${d2}] as const`).join(",\n");
|
|
44536
|
+
const typeImport = "import type { ProgrammaticFieldDefinition } from '@bobbykim/manguito-cms-core'";
|
|
44537
|
+
const body = sorted.length === 0 ? `${typeImport}
|
|
44538
|
+
|
|
44539
|
+
export const programmaticResolvers = new Map<string, ProgrammaticFieldDefinition>()
|
|
44540
|
+
` : `${typeImport}
|
|
44541
|
+
${imports}
|
|
44542
|
+
|
|
44543
|
+
export const programmaticResolvers = new Map<string, ProgrammaticFieldDefinition>([
|
|
44544
|
+
${entries},
|
|
44545
|
+
])
|
|
44546
|
+
`;
|
|
44547
|
+
await writeFile5(join6(targetDir, "programmatic-registry.ts"), body, "utf8");
|
|
44548
|
+
}
|
|
44549
|
+
|
|
44502
44550
|
// src/utils/config.ts
|
|
44503
44551
|
init_esm_shims();
|
|
44504
44552
|
import { existsSync } from "fs";
|
|
@@ -44676,10 +44724,13 @@ async function runBuild(options, deps) {
|
|
|
44676
44724
|
);
|
|
44677
44725
|
const generatedDir = resolve3(cwd, "dist/generated");
|
|
44678
44726
|
mkdirSync(generatedDir, { recursive: true });
|
|
44679
|
-
writeFileSync(
|
|
44727
|
+
writeFileSync(join7(generatedDir, "schema.ts"), generateSchemaFile(registry2), "utf8");
|
|
44680
44728
|
await generateSchemaRegistry(registry2, generatedDir);
|
|
44681
44729
|
await generateRoutes2(registry2, generatedDir);
|
|
44682
|
-
await generateForms(registry2,
|
|
44730
|
+
await generateForms(registry2, join7(generatedDir, "forms"));
|
|
44731
|
+
const programmaticDir = resolve3(cwd, config3.programmatic.dir);
|
|
44732
|
+
const resolverFiles = existsSync3(programmaticDir) ? walkFiles(programmaticDir).filter((f) => /\.(ts|mjs|js)$/.test(f)) : [];
|
|
44733
|
+
await generateProgrammaticRegistry(resolverFiles, generatedDir);
|
|
44683
44734
|
await generateServerEntries(
|
|
44684
44735
|
{ adminPrefix: config3.admin.prefix, apiPrefix: config3.api.prefix },
|
|
44685
44736
|
generatedDir
|
|
@@ -44743,14 +44794,14 @@ async function needsRebuild(cwd) {
|
|
|
44743
44794
|
);
|
|
44744
44795
|
}
|
|
44745
44796
|
function resolveAdminRoot(cwd) {
|
|
44746
|
-
const cwdRequire = createRequire(
|
|
44797
|
+
const cwdRequire = createRequire(join7(cwd, "package.json"));
|
|
44747
44798
|
const adminPkgJson = cwdRequire.resolve("@bobbykim/manguito-cms-admin/package.json");
|
|
44748
44799
|
return dirname(adminPkgJson);
|
|
44749
44800
|
}
|
|
44750
44801
|
function walkFiles(dir) {
|
|
44751
44802
|
const files = [];
|
|
44752
44803
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
44753
|
-
const full =
|
|
44804
|
+
const full = join7(dir, entry.name);
|
|
44754
44805
|
if (entry.isDirectory()) {
|
|
44755
44806
|
files.push(...walkFiles(full));
|
|
44756
44807
|
} else {
|
|
@@ -44763,8 +44814,9 @@ function walkFiles(dir) {
|
|
|
44763
44814
|
// src/commands/dev.ts
|
|
44764
44815
|
init_esm_shims();
|
|
44765
44816
|
import { createServer as createHttpServer } from "http";
|
|
44766
|
-
import {
|
|
44767
|
-
import {
|
|
44817
|
+
import { existsSync as existsSync5 } from "fs";
|
|
44818
|
+
import { resolve as resolve6, join as join11, dirname as dirname2, extname as extname3 } from "path";
|
|
44819
|
+
import { mkdir as mkdir2, writeFile as writeFile8, watch, readFile } from "fs/promises";
|
|
44768
44820
|
import { createRequire as createRequire2 } from "module";
|
|
44769
44821
|
import { createServer as createViteServer, searchForWorkspaceRoot } from "vite";
|
|
44770
44822
|
|
|
@@ -48074,6 +48126,138 @@ function buildRolesRegistry(roles2) {
|
|
|
48074
48126
|
validateRoles(roles2);
|
|
48075
48127
|
return Object.fromEntries(roles2.map((r) => [r.name, r]));
|
|
48076
48128
|
}
|
|
48129
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
48130
|
+
var LIST_CONCURRENCY = 10;
|
|
48131
|
+
function resolverKey(schema, field) {
|
|
48132
|
+
return `${schema}::${field}`;
|
|
48133
|
+
}
|
|
48134
|
+
function validateResolverBindings(registry2, resolvers) {
|
|
48135
|
+
const declared = /* @__PURE__ */ new Set();
|
|
48136
|
+
const schemas = [
|
|
48137
|
+
...Object.values(registry2.content_types),
|
|
48138
|
+
...Object.values(registry2.taxonomy_types)
|
|
48139
|
+
];
|
|
48140
|
+
for (const schema of schemas) {
|
|
48141
|
+
for (const field of schema.fields) {
|
|
48142
|
+
if (field.field_type === "programmatic") {
|
|
48143
|
+
declared.add(resolverKey(schema.name, field.name));
|
|
48144
|
+
}
|
|
48145
|
+
}
|
|
48146
|
+
}
|
|
48147
|
+
const missing = [];
|
|
48148
|
+
for (const key of declared) if (!resolvers.has(key)) missing.push(key);
|
|
48149
|
+
const orphans = [];
|
|
48150
|
+
for (const key of resolvers.keys()) if (!declared.has(key)) orphans.push(key);
|
|
48151
|
+
if (missing.length === 0 && orphans.length === 0) return;
|
|
48152
|
+
const lines = ["\u2717 Programmatic field resolver bindings are invalid."];
|
|
48153
|
+
if (missing.length > 0) {
|
|
48154
|
+
lines.push("", ' Declared as `type: "programmatic"` but no resolver found:');
|
|
48155
|
+
for (const k of missing) lines.push(` - ${k} (add a programmaticField in src/programmatic)`);
|
|
48156
|
+
}
|
|
48157
|
+
if (orphans.length > 0) {
|
|
48158
|
+
lines.push("", " Resolver has no matching programmatic field in the schema:");
|
|
48159
|
+
for (const k of orphans) lines.push(` - ${k}`);
|
|
48160
|
+
}
|
|
48161
|
+
lines.push("", "Exiting.");
|
|
48162
|
+
throw new Error(lines.join("\n"));
|
|
48163
|
+
}
|
|
48164
|
+
function buildContext(row) {
|
|
48165
|
+
return {
|
|
48166
|
+
get: (name) => row[name],
|
|
48167
|
+
record: row
|
|
48168
|
+
};
|
|
48169
|
+
}
|
|
48170
|
+
async function withTimeout(p2, ms2) {
|
|
48171
|
+
let timer;
|
|
48172
|
+
const timeout = new Promise((_, reject) => {
|
|
48173
|
+
timer = setTimeout(() => reject(new Error("resolver timeout")), ms2);
|
|
48174
|
+
});
|
|
48175
|
+
try {
|
|
48176
|
+
return await Promise.race([p2, timeout]);
|
|
48177
|
+
} finally {
|
|
48178
|
+
clearTimeout(timer);
|
|
48179
|
+
}
|
|
48180
|
+
}
|
|
48181
|
+
async function runOne(def, row, onError) {
|
|
48182
|
+
const fallback = def.fallback ?? null;
|
|
48183
|
+
try {
|
|
48184
|
+
const result = await withTimeout(
|
|
48185
|
+
Promise.resolve(def.resolve(buildContext(row))),
|
|
48186
|
+
def.timeout ?? DEFAULT_TIMEOUT_MS
|
|
48187
|
+
);
|
|
48188
|
+
return { value: result === void 0 ? null : result, failed: false };
|
|
48189
|
+
} catch (err) {
|
|
48190
|
+
onError(
|
|
48191
|
+
`\u26A0 programmatic field ${resolverKey(def.schema, def.field)} failed: ${err instanceof Error ? err.message : String(err)}`
|
|
48192
|
+
);
|
|
48193
|
+
return { value: fallback, failed: true };
|
|
48194
|
+
}
|
|
48195
|
+
}
|
|
48196
|
+
async function runPool(items, limit, fn) {
|
|
48197
|
+
let i = 0;
|
|
48198
|
+
async function worker() {
|
|
48199
|
+
while (i < items.length) {
|
|
48200
|
+
const idx = i++;
|
|
48201
|
+
await fn(items[idx]);
|
|
48202
|
+
}
|
|
48203
|
+
}
|
|
48204
|
+
const workers = Array.from({ length: Math.min(limit, items.length) }, worker);
|
|
48205
|
+
await Promise.all(workers);
|
|
48206
|
+
}
|
|
48207
|
+
function createProgrammaticResolver(resolvers, options) {
|
|
48208
|
+
const onError = options?.onError ?? ((message) => {
|
|
48209
|
+
process.stderr.write(message + "\n");
|
|
48210
|
+
});
|
|
48211
|
+
const bySchema = /* @__PURE__ */ new Map();
|
|
48212
|
+
for (const def of resolvers.values()) {
|
|
48213
|
+
const arr = bySchema.get(def.schema) ?? [];
|
|
48214
|
+
arr.push(def);
|
|
48215
|
+
bySchema.set(def.schema, arr);
|
|
48216
|
+
}
|
|
48217
|
+
const cache = /* @__PURE__ */ new Map();
|
|
48218
|
+
async function resolveField(def, row) {
|
|
48219
|
+
const itemId = row["id"] !== void 0 ? String(row["id"]) : "";
|
|
48220
|
+
if (def.cache && itemId) {
|
|
48221
|
+
const key = `${resolverKey(def.schema, def.field)}::${itemId}`;
|
|
48222
|
+
const now = Date.now();
|
|
48223
|
+
const hit = cache.get(key);
|
|
48224
|
+
if (hit && hit.expires > now) return hit.value;
|
|
48225
|
+
const { value: value2, failed } = await runOne(def, row, onError);
|
|
48226
|
+
if (!failed) cache.set(key, { value: value2, expires: now + def.cache.ttl * 1e3 });
|
|
48227
|
+
return value2;
|
|
48228
|
+
}
|
|
48229
|
+
const { value } = await runOne(def, row, onError);
|
|
48230
|
+
return value;
|
|
48231
|
+
}
|
|
48232
|
+
async function resolveItem(schema, row) {
|
|
48233
|
+
const defs = bySchema.get(schema);
|
|
48234
|
+
if (!defs || defs.length === 0) return { ...row };
|
|
48235
|
+
const out = { ...row };
|
|
48236
|
+
await Promise.all(
|
|
48237
|
+
defs.map(async (def) => {
|
|
48238
|
+
out[def.field] = await resolveField(def, row);
|
|
48239
|
+
})
|
|
48240
|
+
);
|
|
48241
|
+
return out;
|
|
48242
|
+
}
|
|
48243
|
+
async function resolveList(schema, rows) {
|
|
48244
|
+
const defs = (bySchema.get(schema) ?? []).filter((d2) => d2.on_list === true);
|
|
48245
|
+
if (defs.length === 0) return rows.map((r) => ({ ...r }));
|
|
48246
|
+
const out = rows.map((r) => ({ ...r }));
|
|
48247
|
+
const tasks = [];
|
|
48248
|
+
for (let i = 0; i < rows.length; i++) {
|
|
48249
|
+
for (const def of defs) tasks.push({ rowIndex: i, def });
|
|
48250
|
+
}
|
|
48251
|
+
await runPool(tasks, LIST_CONCURRENCY, async ({ rowIndex, def }) => {
|
|
48252
|
+
out[rowIndex][def.field] = await resolveField(def, rows[rowIndex]);
|
|
48253
|
+
});
|
|
48254
|
+
return out;
|
|
48255
|
+
}
|
|
48256
|
+
function hasSchema(schema) {
|
|
48257
|
+
return bySchema.has(schema);
|
|
48258
|
+
}
|
|
48259
|
+
return { resolveItem, resolveList, hasSchema };
|
|
48260
|
+
}
|
|
48077
48261
|
var SORTABLE_FIELDS = /* @__PURE__ */ new Set(["title", "created_at", "updated_at"]);
|
|
48078
48262
|
var RELATION_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
48079
48263
|
"paragraph",
|
|
@@ -48122,7 +48306,7 @@ function parseFilters(url2, validFields) {
|
|
|
48122
48306
|
function isPublished(item) {
|
|
48123
48307
|
return item["published"] === true;
|
|
48124
48308
|
}
|
|
48125
|
-
function registerPublicContentRoutes(app, registry2, repos, listRateLimit) {
|
|
48309
|
+
function registerPublicContentRoutes(app, registry2, repos, listRateLimit, resolver) {
|
|
48126
48310
|
function registerListRoute(path4, handler) {
|
|
48127
48311
|
if (listRateLimit) {
|
|
48128
48312
|
app.get(path4, listRateLimit, handler);
|
|
@@ -48149,13 +48333,13 @@ function registerPublicContentRoutes(app, registry2, repos, listRateLimit) {
|
|
|
48149
48333
|
const basePath = contentType.default_base_path;
|
|
48150
48334
|
const repo = repos[typeName];
|
|
48151
48335
|
if (!repo) continue;
|
|
48152
|
-
const schemaFieldNames = /* @__PURE__ */ new Set([
|
|
48153
|
-
...contentType.fields.map((f) => f.name),
|
|
48154
|
-
...contentType.system_fields.map((f) => f.name)
|
|
48155
|
-
]);
|
|
48156
48336
|
const relationFieldNames = new Set(
|
|
48157
48337
|
contentType.fields.filter((f) => RELATION_FIELD_TYPES.has(f.field_type)).map((f) => f.name)
|
|
48158
48338
|
);
|
|
48339
|
+
const filterableFieldNames = /* @__PURE__ */ new Set([
|
|
48340
|
+
...contentType.fields.filter((f) => f.field_type !== "programmatic").map((f) => f.name),
|
|
48341
|
+
...contentType.system_fields.map((f) => f.name)
|
|
48342
|
+
]);
|
|
48159
48343
|
if (contentType.only_one) {
|
|
48160
48344
|
app.get(`/api/${basePath}`, async (c) => {
|
|
48161
48345
|
const result = await repo.findMany({ published_only: true, page: 1, per_page: 1 });
|
|
@@ -48165,7 +48349,9 @@ function registerPublicContentRoutes(app, registry2, repos, listRateLimit) {
|
|
|
48165
48349
|
404
|
|
48166
48350
|
);
|
|
48167
48351
|
}
|
|
48168
|
-
|
|
48352
|
+
let data = result.data[0];
|
|
48353
|
+
if (resolver?.hasSchema(typeName)) data = await resolver.resolveItem(typeName, data);
|
|
48354
|
+
return c.json({ ok: true, data });
|
|
48169
48355
|
});
|
|
48170
48356
|
} else {
|
|
48171
48357
|
registerListRoute(`/api/${basePath}`, async (c) => {
|
|
@@ -48208,7 +48394,7 @@ function registerPublicContentRoutes(app, registry2, repos, listRateLimit) {
|
|
|
48208
48394
|
400
|
|
48209
48395
|
);
|
|
48210
48396
|
}
|
|
48211
|
-
const filtersResult = parseFilters(c.req.url,
|
|
48397
|
+
const filtersResult = parseFilters(c.req.url, filterableFieldNames);
|
|
48212
48398
|
if (!filtersResult.ok) {
|
|
48213
48399
|
return c.json(
|
|
48214
48400
|
{
|
|
@@ -48245,6 +48431,10 @@ function registerPublicContentRoutes(app, registry2, repos, listRateLimit) {
|
|
|
48245
48431
|
filters: filtersResult.filters,
|
|
48246
48432
|
include
|
|
48247
48433
|
});
|
|
48434
|
+
if (resolver?.hasSchema(typeName)) {
|
|
48435
|
+
const resolved = await resolver.resolveList(typeName, result.data);
|
|
48436
|
+
return c.json({ ...result, data: resolved });
|
|
48437
|
+
}
|
|
48248
48438
|
return c.json(result);
|
|
48249
48439
|
});
|
|
48250
48440
|
app.get(`/api/${basePath}/:slug`, async (c) => {
|
|
@@ -48274,7 +48464,9 @@ function registerPublicContentRoutes(app, registry2, repos, listRateLimit) {
|
|
|
48274
48464
|
404
|
|
48275
48465
|
);
|
|
48276
48466
|
}
|
|
48277
|
-
|
|
48467
|
+
let data = item;
|
|
48468
|
+
if (resolver?.hasSchema(typeName)) data = await resolver.resolveItem(typeName, data);
|
|
48469
|
+
return c.json({ ok: true, data });
|
|
48278
48470
|
});
|
|
48279
48471
|
}
|
|
48280
48472
|
}
|
|
@@ -48300,6 +48492,10 @@ function registerPublicContentRoutes(app, registry2, repos, listRateLimit) {
|
|
|
48300
48492
|
page: pagination.page,
|
|
48301
48493
|
per_page: pagination.per_page
|
|
48302
48494
|
});
|
|
48495
|
+
if (resolver?.hasSchema(typeName)) {
|
|
48496
|
+
const resolved = await resolver.resolveList(typeName, result.data);
|
|
48497
|
+
return c.json({ ...result, data: resolved });
|
|
48498
|
+
}
|
|
48303
48499
|
return c.json(result);
|
|
48304
48500
|
});
|
|
48305
48501
|
app.get(`/api/taxonomy/${typeName}/:id`, async (c) => {
|
|
@@ -48314,7 +48510,9 @@ function registerPublicContentRoutes(app, registry2, repos, listRateLimit) {
|
|
|
48314
48510
|
404
|
|
48315
48511
|
);
|
|
48316
48512
|
}
|
|
48317
|
-
|
|
48513
|
+
let data = item;
|
|
48514
|
+
if (resolver?.hasSchema(typeName)) data = await resolver.resolveItem(typeName, data);
|
|
48515
|
+
return c.json({ ok: true, data });
|
|
48318
48516
|
});
|
|
48319
48517
|
}
|
|
48320
48518
|
}
|
|
@@ -50238,7 +50436,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
50238
50436
|
}
|
|
50239
50437
|
var API_VERSION = (() => {
|
|
50240
50438
|
try {
|
|
50241
|
-
return "0.
|
|
50439
|
+
return "0.2.0";
|
|
50242
50440
|
} catch {
|
|
50243
50441
|
return "0.0.0";
|
|
50244
50442
|
}
|
|
@@ -50641,6 +50839,9 @@ function createCmsApp(options) {
|
|
|
50641
50839
|
const cmsName = options.name ?? "Manguito CMS";
|
|
50642
50840
|
const maxFileSize = media?.max_file_size;
|
|
50643
50841
|
const rolesRegistry = buildRolesRegistry(registry2.roles.roles);
|
|
50842
|
+
const resolverMap = options.resolvers ?? /* @__PURE__ */ new Map();
|
|
50843
|
+
validateResolverBindings(registry2, resolverMap);
|
|
50844
|
+
const programmaticResolver = createProgrammaticResolver(resolverMap);
|
|
50644
50845
|
const app = new Hono2();
|
|
50645
50846
|
const uploadOrigins = storage.getUploadOrigins?.() ?? [];
|
|
50646
50847
|
app.use("*", createSecurityHeadersMiddleware({ connectSrc: uploadOrigins }));
|
|
@@ -50725,7 +50926,7 @@ function createCmsApp(options) {
|
|
|
50725
50926
|
paths: {}
|
|
50726
50927
|
})
|
|
50727
50928
|
);
|
|
50728
|
-
registerPublicContentRoutes(app, registry2, publicRepos, listRateLimit);
|
|
50929
|
+
registerPublicContentRoutes(app, registry2, publicRepos, listRateLimit, programmaticResolver);
|
|
50729
50930
|
registerPublicMediaRoutes(app, mediaRepo, listRateLimit);
|
|
50730
50931
|
registerConfigRoute(
|
|
50731
50932
|
app,
|
|
@@ -50750,12 +50951,12 @@ function createCmsApp(options) {
|
|
|
50750
50951
|
|
|
50751
50952
|
// src/codegen/drizzle-config.ts
|
|
50752
50953
|
init_esm_shims();
|
|
50753
|
-
import { writeFile as
|
|
50754
|
-
import { join as
|
|
50954
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
50955
|
+
import { join as join8, relative as relative2, resolve as resolve4 } from "path";
|
|
50755
50956
|
async function generateDrizzleConfig(config3, targetDir) {
|
|
50756
50957
|
const migrationsFolder = config3.migrations?.folder ?? "./migrations";
|
|
50757
50958
|
const migrationsTable = config3.migrations?.table ?? "__manguito_migrations";
|
|
50758
|
-
const outPath =
|
|
50959
|
+
const outPath = relative2(resolve4(targetDir), resolve4(migrationsFolder)).replace(/\\/g, "/");
|
|
50759
50960
|
const content = [
|
|
50760
50961
|
`import { defineConfig } from 'drizzle-kit'`,
|
|
50761
50962
|
``,
|
|
@@ -50773,13 +50974,13 @@ async function generateDrizzleConfig(config3, targetDir) {
|
|
|
50773
50974
|
` tablesFilter: ['!${migrationsTable}'],`,
|
|
50774
50975
|
`})`
|
|
50775
50976
|
].join("\n");
|
|
50776
|
-
await
|
|
50977
|
+
await writeFile6(join8(targetDir, "drizzle.config.ts"), content, "utf8");
|
|
50777
50978
|
}
|
|
50778
50979
|
|
|
50779
50980
|
// src/codegen/nav.ts
|
|
50780
50981
|
init_esm_shims();
|
|
50781
|
-
import { writeFile as
|
|
50782
|
-
import { join as
|
|
50982
|
+
import { writeFile as writeFile7 } from "fs/promises";
|
|
50983
|
+
import { join as join9 } from "path";
|
|
50783
50984
|
function schemaBasePath(name) {
|
|
50784
50985
|
const segment = name.includes("--") ? name.slice(name.indexOf("--") + 2) : name;
|
|
50785
50986
|
return segment.replace(/_/g, "-");
|
|
@@ -50797,7 +50998,7 @@ async function generateNav(registry2, targetDir) {
|
|
|
50797
50998
|
})).sort((a2, b2) => a2.label.localeCompare(b2.label));
|
|
50798
50999
|
const content = `export const nav = ${JSON.stringify({ contentTypes, taxonomyTypes }, null, 2)}
|
|
50799
51000
|
`;
|
|
50800
|
-
await
|
|
51001
|
+
await writeFile7(join9(targetDir, "nav.ts"), content, "utf8");
|
|
50801
51002
|
}
|
|
50802
51003
|
|
|
50803
51004
|
// src/utils/db.ts
|
|
@@ -50828,6 +51029,49 @@ function createPromptAdapter() {
|
|
|
50828
51029
|
};
|
|
50829
51030
|
}
|
|
50830
51031
|
|
|
51032
|
+
// src/utils/programmatic-loader.ts
|
|
51033
|
+
init_esm_shims();
|
|
51034
|
+
import { existsSync as existsSync4 } from "fs";
|
|
51035
|
+
import { readdir } from "fs/promises";
|
|
51036
|
+
import { resolve as resolve5, join as join10, extname as extname2 } from "path";
|
|
51037
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
51038
|
+
var RESOLVER_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".mjs", ".js"]);
|
|
51039
|
+
function isProgrammaticFieldDefinition(v2) {
|
|
51040
|
+
return v2 !== null && typeof v2 === "object" && v2.__manguito_programmatic === true && typeof v2.schema === "string" && typeof v2.field === "string" && typeof v2.resolve === "function";
|
|
51041
|
+
}
|
|
51042
|
+
async function walkTsFiles(dir) {
|
|
51043
|
+
const out = [];
|
|
51044
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
51045
|
+
const full = join10(dir, entry.name);
|
|
51046
|
+
if (entry.isDirectory()) {
|
|
51047
|
+
out.push(...await walkTsFiles(full));
|
|
51048
|
+
} else if (RESOLVER_EXTENSIONS.has(extname2(entry.name))) {
|
|
51049
|
+
out.push(full);
|
|
51050
|
+
}
|
|
51051
|
+
}
|
|
51052
|
+
return out;
|
|
51053
|
+
}
|
|
51054
|
+
async function loadProgrammaticResolvers(cwd, dir) {
|
|
51055
|
+
const abs = resolve5(cwd, dir);
|
|
51056
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
51057
|
+
if (!existsSync4(abs)) return map2;
|
|
51058
|
+
for (const file2 of (await walkTsFiles(abs)).sort()) {
|
|
51059
|
+
const mod = await import(pathToFileURL2(file2).href);
|
|
51060
|
+
const def = mod.default;
|
|
51061
|
+
if (!isProgrammaticFieldDefinition(def)) {
|
|
51062
|
+
throw new Error(
|
|
51063
|
+
`\u2717 ${file2} does not default-export a programmaticField(). Each resolver file must \`export default programmaticField({ schema, field }, resolver)\`.`
|
|
51064
|
+
);
|
|
51065
|
+
}
|
|
51066
|
+
const key = resolverKey(def.schema, def.field);
|
|
51067
|
+
if (map2.has(key)) {
|
|
51068
|
+
throw new Error(`\u2717 Duplicate programmatic resolver for ${key} (found again in ${file2}).`);
|
|
51069
|
+
}
|
|
51070
|
+
map2.set(key, def);
|
|
51071
|
+
}
|
|
51072
|
+
return map2;
|
|
51073
|
+
}
|
|
51074
|
+
|
|
50831
51075
|
// src/commands/dev.ts
|
|
50832
51076
|
var UPLOAD_MIME_MAP = {
|
|
50833
51077
|
jpg: "image/jpeg",
|
|
@@ -50855,13 +51099,13 @@ async function runDev(options, deps) {
|
|
|
50855
51099
|
printSuccess("Config loaded");
|
|
50856
51100
|
const db = await connectDb(config3);
|
|
50857
51101
|
printSuccess("Database connected");
|
|
50858
|
-
const manguitoDir =
|
|
51102
|
+
const manguitoDir = resolve6(cwd, ".manguito");
|
|
50859
51103
|
await mkdir2(manguitoDir, { recursive: true });
|
|
50860
|
-
const drizzleConfigPath =
|
|
51104
|
+
const drizzleConfigPath = resolve6(manguitoDir, "drizzle.config.ts");
|
|
50861
51105
|
const needsSeed = !await db.tableExists("users");
|
|
50862
51106
|
const bootstrapRegistry = await parseAllSchemas(cwd, config3);
|
|
50863
51107
|
await generateDrizzleConfig(config3, manguitoDir);
|
|
50864
|
-
await
|
|
51108
|
+
await writeFile8(join11(manguitoDir, "schema.ts"), generateSchemaFile(bootstrapRegistry), "utf8");
|
|
50865
51109
|
await runDevMigration(drizzleConfigPath);
|
|
50866
51110
|
printSuccess(needsSeed ? "Schema tables created" : "Schema tables up to date");
|
|
50867
51111
|
if (needsSeed) {
|
|
@@ -50907,13 +51151,15 @@ async function runDev(options, deps) {
|
|
|
50907
51151
|
);
|
|
50908
51152
|
await generateSchemaRegistry(registry2, manguitoDir);
|
|
50909
51153
|
await generateRoutes2(registry2, manguitoDir);
|
|
50910
|
-
await generateForms(registry2,
|
|
51154
|
+
await generateForms(registry2, join11(manguitoDir, "forms"));
|
|
50911
51155
|
await generateNav(registry2, manguitoDir);
|
|
51156
|
+
const resolverMap = await loadProgrammaticResolvers(cwd, config3.programmatic.dir);
|
|
50912
51157
|
const adapter = createCmsApp({
|
|
50913
51158
|
name: config3.name,
|
|
50914
51159
|
registry: registry2,
|
|
50915
51160
|
db: db.getDb(),
|
|
50916
51161
|
storage: config3.storage,
|
|
51162
|
+
resolvers: resolverMap,
|
|
50917
51163
|
...config3.api.prefix ? { prefix: config3.api.prefix } : {},
|
|
50918
51164
|
...config3.api.media?.max_file_size ? { media: { max_file_size: config3.api.media.max_file_size } } : {},
|
|
50919
51165
|
...config3.api.rateLimit ? { rateLimit: config3.api.rateLimit } : {},
|
|
@@ -50945,8 +51191,8 @@ async function runDev(options, deps) {
|
|
|
50945
51191
|
}
|
|
50946
51192
|
if (url2.startsWith("/uploads/")) {
|
|
50947
51193
|
const relativePath = (url2.slice("/uploads/".length).split("?")[0] ?? "").replace(/\.\./g, "");
|
|
50948
|
-
const uploadsDir =
|
|
50949
|
-
const filePath =
|
|
51194
|
+
const uploadsDir = resolve6(cwd, "uploads");
|
|
51195
|
+
const filePath = resolve6(uploadsDir, relativePath);
|
|
50950
51196
|
if (!filePath.startsWith(uploadsDir + "/") && filePath !== uploadsDir) {
|
|
50951
51197
|
res.statusCode = 403;
|
|
50952
51198
|
res.end("Forbidden");
|
|
@@ -50954,7 +51200,7 @@ async function runDev(options, deps) {
|
|
|
50954
51200
|
}
|
|
50955
51201
|
try {
|
|
50956
51202
|
const data = await readFile(filePath);
|
|
50957
|
-
const ext =
|
|
51203
|
+
const ext = extname3(filePath).slice(1).toLowerCase();
|
|
50958
51204
|
const mime = UPLOAD_MIME_MAP[ext] ?? "application/octet-stream";
|
|
50959
51205
|
res.setHeader("Content-Type", mime);
|
|
50960
51206
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
@@ -50987,7 +51233,7 @@ async function runDev(options, deps) {
|
|
|
50987
51233
|
printSuccess(`Dev server running at http://localhost:${port}`);
|
|
50988
51234
|
printSuccess(`Admin panel at http://localhost:${port}${config3.admin.prefix}`);
|
|
50989
51235
|
});
|
|
50990
|
-
const schemasDir =
|
|
51236
|
+
const schemasDir = resolve6(cwd, config3.schema.base_path);
|
|
50991
51237
|
const watcher = watch(schemasDir, { recursive: true });
|
|
50992
51238
|
void (async () => {
|
|
50993
51239
|
for await (const event of watcher) {
|
|
@@ -51000,14 +51246,36 @@ async function runDev(options, deps) {
|
|
|
51000
51246
|
db,
|
|
51001
51247
|
updateFetch: (fetch2) => {
|
|
51002
51248
|
honoFetch = fetch2;
|
|
51003
|
-
}
|
|
51249
|
+
},
|
|
51250
|
+
getResolvers: () => loadProgrammaticResolvers(cwd, config3.programmatic.dir)
|
|
51004
51251
|
});
|
|
51005
51252
|
}
|
|
51006
51253
|
}
|
|
51007
51254
|
})();
|
|
51255
|
+
const programmaticDir = resolve6(cwd, config3.programmatic.dir);
|
|
51256
|
+
if (existsSync5(programmaticDir)) {
|
|
51257
|
+
const progWatcher = watch(programmaticDir, { recursive: true });
|
|
51258
|
+
void (async () => {
|
|
51259
|
+
for await (const event of progWatcher) {
|
|
51260
|
+
if (event.filename && /\.(ts|mjs|js)$/.test(event.filename)) {
|
|
51261
|
+
await onSchemaFileChange({
|
|
51262
|
+
cwd,
|
|
51263
|
+
config: config3,
|
|
51264
|
+
manguitoDir,
|
|
51265
|
+
drizzleConfigPath,
|
|
51266
|
+
db,
|
|
51267
|
+
updateFetch: (fetch2) => {
|
|
51268
|
+
honoFetch = fetch2;
|
|
51269
|
+
},
|
|
51270
|
+
getResolvers: () => loadProgrammaticResolvers(cwd, config3.programmatic.dir)
|
|
51271
|
+
});
|
|
51272
|
+
}
|
|
51273
|
+
}
|
|
51274
|
+
})();
|
|
51275
|
+
}
|
|
51008
51276
|
}
|
|
51009
51277
|
async function onSchemaFileChange(args) {
|
|
51010
|
-
const { cwd, config: config3, manguitoDir, drizzleConfigPath, db, updateFetch } = args;
|
|
51278
|
+
const { cwd, config: config3, manguitoDir, drizzleConfigPath, db, updateFetch, getResolvers } = args;
|
|
51011
51279
|
let registry2;
|
|
51012
51280
|
try {
|
|
51013
51281
|
registry2 = await parseAllSchemas(cwd, config3);
|
|
@@ -51016,10 +51284,10 @@ async function onSchemaFileChange(args) {
|
|
|
51016
51284
|
return;
|
|
51017
51285
|
}
|
|
51018
51286
|
await generateDrizzleConfig(config3, manguitoDir);
|
|
51019
|
-
await
|
|
51287
|
+
await writeFile8(join11(manguitoDir, "schema.ts"), generateSchemaFile(registry2), "utf8");
|
|
51020
51288
|
await generateSchemaRegistry(registry2, manguitoDir);
|
|
51021
51289
|
await generateRoutes2(registry2, manguitoDir);
|
|
51022
|
-
await generateForms(registry2,
|
|
51290
|
+
await generateForms(registry2, join11(manguitoDir, "forms"));
|
|
51023
51291
|
await generateNav(registry2, manguitoDir);
|
|
51024
51292
|
try {
|
|
51025
51293
|
await runDevMigration(drizzleConfigPath);
|
|
@@ -51028,11 +51296,13 @@ async function onSchemaFileChange(args) {
|
|
|
51028
51296
|
`);
|
|
51029
51297
|
return;
|
|
51030
51298
|
}
|
|
51299
|
+
const resolverMap = await getResolvers();
|
|
51031
51300
|
const newAdapter = createCmsApp({
|
|
51032
51301
|
name: config3.name,
|
|
51033
51302
|
registry: registry2,
|
|
51034
51303
|
db: db.getDb(),
|
|
51035
51304
|
storage: config3.storage,
|
|
51305
|
+
resolvers: resolverMap,
|
|
51036
51306
|
...config3.api.prefix ? { prefix: config3.api.prefix } : {},
|
|
51037
51307
|
...config3.api.media?.max_file_size ? { media: { max_file_size: config3.api.media.max_file_size } } : {},
|
|
51038
51308
|
...config3.api.rateLimit ? { rateLimit: config3.api.rateLimit } : {},
|
|
@@ -51056,7 +51326,7 @@ async function parseAllSchemas(cwd, config3) {
|
|
|
51056
51326
|
}
|
|
51057
51327
|
parsedSchemas.push(parseResult.schema);
|
|
51058
51328
|
}
|
|
51059
|
-
const rolesPath =
|
|
51329
|
+
const rolesPath = resolve6(cwd, config3.schema.base_path, "roles.json");
|
|
51060
51330
|
const rolesLoad = loadSchemaFile(rolesPath);
|
|
51061
51331
|
if (!rolesLoad.ok) {
|
|
51062
51332
|
printGuidedError("Could not read roles.json.");
|
|
@@ -51067,7 +51337,7 @@ async function parseAllSchemas(cwd, config3) {
|
|
|
51067
51337
|
printGuidedError("roles.json parse errors \u2014 run `manguito validate` for details.");
|
|
51068
51338
|
process.exit(1);
|
|
51069
51339
|
}
|
|
51070
|
-
const routesPath =
|
|
51340
|
+
const routesPath = resolve6(cwd, config3.schema.base_path, "routes.json");
|
|
51071
51341
|
const routesLoad = loadSchemaFile(routesPath);
|
|
51072
51342
|
if (!routesLoad.ok) {
|
|
51073
51343
|
printGuidedError("Could not read routes.json.");
|
|
@@ -51081,7 +51351,7 @@ async function parseAllSchemas(cwd, config3) {
|
|
|
51081
51351
|
return buildSchemaRegistry(parsedSchemas, routesResult.value, rolesResult.value);
|
|
51082
51352
|
}
|
|
51083
51353
|
function resolveAdminRoot2(cwd) {
|
|
51084
|
-
const cwdRequire = createRequire2(
|
|
51354
|
+
const cwdRequire = createRequire2(join11(cwd, "package.json"));
|
|
51085
51355
|
const adminPkgJson = cwdRequire.resolve("@bobbykim/manguito-cms-admin/package.json");
|
|
51086
51356
|
return dirname2(adminPkgJson);
|
|
51087
51357
|
}
|
|
@@ -51127,8 +51397,8 @@ async function bridgeToHono(req, res, honoFetch) {
|
|
|
51127
51397
|
|
|
51128
51398
|
// src/commands/start.ts
|
|
51129
51399
|
init_esm_shims();
|
|
51130
|
-
import { resolve as
|
|
51131
|
-
import { pathToFileURL as
|
|
51400
|
+
import { resolve as resolve7 } from "path";
|
|
51401
|
+
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
51132
51402
|
function registerStart(program2) {
|
|
51133
51403
|
program2.command("start").description("Start the production server from dist/").option("--env <path>", "path to .env file to load").action(async (options) => {
|
|
51134
51404
|
await runStart(options, { cwd: process.cwd() });
|
|
@@ -51140,7 +51410,7 @@ async function runStart(options, deps) {
|
|
|
51140
51410
|
const config3 = await resolveConfig(cwd);
|
|
51141
51411
|
const db = await connectDb(config3);
|
|
51142
51412
|
const migrationsTable = config3.migrations?.table ?? "__manguito_migrations";
|
|
51143
|
-
const migrationsFolder = config3.migrations?.folder ?
|
|
51413
|
+
const migrationsFolder = config3.migrations?.folder ? resolve7(cwd, config3.migrations.folder) : resolve7(cwd, "./migrations");
|
|
51144
51414
|
const trackingTableExists = await db.tableExists(migrationsTable);
|
|
51145
51415
|
if (!trackingTableExists) {
|
|
51146
51416
|
printGuidedError(
|
|
@@ -51175,7 +51445,7 @@ async function runStart(options, deps) {
|
|
|
51175
51445
|
} else {
|
|
51176
51446
|
allErrors.push(...walkResult.errors);
|
|
51177
51447
|
}
|
|
51178
|
-
const rolesPath =
|
|
51448
|
+
const rolesPath = resolve7(cwd, config3.schema.base_path, "roles.json");
|
|
51179
51449
|
const rolesLoad = loadSchemaFile(rolesPath);
|
|
51180
51450
|
let parsedRoles = null;
|
|
51181
51451
|
if (rolesLoad.ok) {
|
|
@@ -51188,7 +51458,7 @@ async function runStart(options, deps) {
|
|
|
51188
51458
|
} else {
|
|
51189
51459
|
allErrors.push(...rolesLoad.errors);
|
|
51190
51460
|
}
|
|
51191
|
-
const routesPath =
|
|
51461
|
+
const routesPath = resolve7(cwd, config3.schema.base_path, "routes.json");
|
|
51192
51462
|
const routesLoad = loadSchemaFile(routesPath);
|
|
51193
51463
|
let parsedRoutes = null;
|
|
51194
51464
|
if (routesLoad.ok) {
|
|
@@ -51210,7 +51480,7 @@ async function runStart(options, deps) {
|
|
|
51210
51480
|
}
|
|
51211
51481
|
const registry2 = buildSchemaRegistry(parsedSchemas, parsedRoutes, parsedRoles);
|
|
51212
51482
|
await seedSystemTables(db.getDb(), registry2);
|
|
51213
|
-
const serverUrl =
|
|
51483
|
+
const serverUrl = pathToFileURL3(resolve7(cwd, "dist/server.js")).href;
|
|
51214
51484
|
await (deps.loadServer ? deps.loadServer(serverUrl) : import(serverUrl));
|
|
51215
51485
|
const port = Number(process.env["PORT"] ?? 3e3);
|
|
51216
51486
|
printSuccess(`Server running at http://localhost:${port}`);
|
|
@@ -51219,7 +51489,7 @@ async function runStart(options, deps) {
|
|
|
51219
51489
|
|
|
51220
51490
|
// src/commands/migrate.ts
|
|
51221
51491
|
init_esm_shims();
|
|
51222
|
-
import { resolve as
|
|
51492
|
+
import { resolve as resolve8, join as join12 } from "path";
|
|
51223
51493
|
import { readFile as readFile2 } from "fs/promises";
|
|
51224
51494
|
function registerMigrate(program2) {
|
|
51225
51495
|
program2.command("migrate").description("Apply pending database migrations").option("--env <path>", "path to .env file to load").option("--status", "show migration state without applying").option("--dry-run", "preview migrations without writing").option("--force", "skip destructive-change confirmation prompt").action(async (options) => {
|
|
@@ -51237,8 +51507,8 @@ function buildDeps(options, cwd) {
|
|
|
51237
51507
|
buildRunner: async () => runBuild(options.env ? { env: options.env } : {}, { cwd }),
|
|
51238
51508
|
needsRebuild: () => needsRebuild(cwd),
|
|
51239
51509
|
db: createPostgresAdapter(),
|
|
51240
|
-
migrationsFolder:
|
|
51241
|
-
configPath:
|
|
51510
|
+
migrationsFolder: resolve8(cwd, "./migrations"),
|
|
51511
|
+
configPath: resolve8(cwd, "dist/generated/drizzle.config.ts"),
|
|
51242
51512
|
prompt: createPromptAdapter()
|
|
51243
51513
|
};
|
|
51244
51514
|
}
|
|
@@ -51253,7 +51523,7 @@ async function runMigrate(options, deps) {
|
|
|
51253
51523
|
}
|
|
51254
51524
|
const config4 = await resolveConfig(process.cwd());
|
|
51255
51525
|
const migrationsTable2 = config4.migrations?.table ?? "__manguito_migrations";
|
|
51256
|
-
const migrationsFolder2 = config4.migrations?.folder ?
|
|
51526
|
+
const migrationsFolder2 = config4.migrations?.folder ? resolve8(process.cwd(), config4.migrations.folder) : deps.migrationsFolder;
|
|
51257
51527
|
const status = await getMigrationStatus(deps.db.getDb(), { migrationsTable: migrationsTable2, migrationsFolder: migrationsFolder2 });
|
|
51258
51528
|
if (status.applied.length === 0 && status.pending.length === 0) {
|
|
51259
51529
|
process.stdout.write(
|
|
@@ -51285,7 +51555,7 @@ async function runMigrate(options, deps) {
|
|
|
51285
51555
|
loadEnvFile(options.env);
|
|
51286
51556
|
const cwd = process.cwd();
|
|
51287
51557
|
const config3 = await resolveConfig(cwd);
|
|
51288
|
-
const migrationsFolder = config3.migrations?.folder ?
|
|
51558
|
+
const migrationsFolder = config3.migrations?.folder ? resolve8(cwd, config3.migrations.folder) : deps.migrationsFolder;
|
|
51289
51559
|
const migrationsTable = config3.migrations?.table ?? "__manguito_migrations";
|
|
51290
51560
|
try {
|
|
51291
51561
|
await deps.db.connect();
|
|
@@ -51305,10 +51575,10 @@ async function runMigrate(options, deps) {
|
|
|
51305
51575
|
}
|
|
51306
51576
|
}
|
|
51307
51577
|
printSuccess("Build complete");
|
|
51308
|
-
const generatedDir =
|
|
51578
|
+
const generatedDir = resolve8(cwd, "dist/generated");
|
|
51309
51579
|
await generateDrizzleConfig(config3, generatedDir);
|
|
51310
51580
|
const newFileNames = await generateMigration(deps.configPath, migrationsFolder);
|
|
51311
|
-
const newFilePaths = newFileNames.map((f) =>
|
|
51581
|
+
const newFilePaths = newFileNames.map((f) => join12(migrationsFolder, f));
|
|
51312
51582
|
printSuccess(
|
|
51313
51583
|
newFileNames.length === 0 ? "No new migration files generated" : `Generated ${newFileNames.length} migration file${newFileNames.length !== 1 ? "s" : ""}: ${newFileNames.join(", ")}`
|
|
51314
51584
|
);
|
|
@@ -51378,7 +51648,7 @@ async function runMigrate(options, deps) {
|
|
|
51378
51648
|
} else {
|
|
51379
51649
|
allErrors.push(...walkResult.errors);
|
|
51380
51650
|
}
|
|
51381
|
-
const rolesPath =
|
|
51651
|
+
const rolesPath = resolve8(cwd, config3.schema.base_path, "roles.json");
|
|
51382
51652
|
const rolesLoad = loadSchemaFile(rolesPath);
|
|
51383
51653
|
let parsedRoles = null;
|
|
51384
51654
|
if (rolesLoad.ok) {
|
|
@@ -51391,7 +51661,7 @@ async function runMigrate(options, deps) {
|
|
|
51391
51661
|
} else {
|
|
51392
51662
|
allErrors.push(...rolesLoad.errors);
|
|
51393
51663
|
}
|
|
51394
|
-
const routesPath =
|
|
51664
|
+
const routesPath = resolve8(cwd, config3.schema.base_path, "routes.json");
|
|
51395
51665
|
const routesLoad = loadSchemaFile(routesPath);
|
|
51396
51666
|
let parsedRoutes = null;
|
|
51397
51667
|
if (routesLoad.ok) {
|
|
@@ -51423,7 +51693,7 @@ async function runMigrate(options, deps) {
|
|
|
51423
51693
|
|
|
51424
51694
|
// src/commands/validate.ts
|
|
51425
51695
|
init_esm_shims();
|
|
51426
|
-
import { resolve as
|
|
51696
|
+
import { resolve as resolve9 } from "path";
|
|
51427
51697
|
function registerValidate(program2) {
|
|
51428
51698
|
program2.command("validate").description("Parse and validate all schemas, config, roles, and routes").option("--env <path>", "path to .env file to load").action(async (options) => {
|
|
51429
51699
|
await runValidate(options, { cwd: process.cwd() });
|
|
@@ -51448,7 +51718,7 @@ async function runValidate(options, deps) {
|
|
|
51448
51718
|
}
|
|
51449
51719
|
}
|
|
51450
51720
|
}
|
|
51451
|
-
const rolesPath =
|
|
51721
|
+
const rolesPath = resolve9(cwd, config3.schema.base_path, "roles.json");
|
|
51452
51722
|
const rolesLoadResult = loadSchemaFile(rolesPath);
|
|
51453
51723
|
let parsedRoles = null;
|
|
51454
51724
|
if (!rolesLoadResult.ok) {
|
|
@@ -51461,7 +51731,7 @@ async function runValidate(options, deps) {
|
|
|
51461
51731
|
parsedRoles = rolesParseResult.value;
|
|
51462
51732
|
}
|
|
51463
51733
|
}
|
|
51464
|
-
const routesPath =
|
|
51734
|
+
const routesPath = resolve9(cwd, config3.schema.base_path, "routes.json");
|
|
51465
51735
|
const routesLoadResult = loadSchemaFile(routesPath);
|
|
51466
51736
|
let parsedRoutesDef = null;
|
|
51467
51737
|
if (!routesLoadResult.ok) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobbykim/manguito-cms-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Bobby Kim <bobby.sihun.kim@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/bobbykim89/manguito-cms",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"tsup": "^8.5.1",
|
|
30
30
|
"typescript": "^6.0.3",
|
|
31
31
|
"vite": "^8.1.3",
|
|
32
|
-
"@bobbykim/manguito-cms-
|
|
33
|
-
"@bobbykim/manguito-cms-
|
|
34
|
-
"@bobbykim/manguito-cms-db": "0.1.
|
|
35
|
-
"@bobbykim/manguito-cms-
|
|
32
|
+
"@bobbykim/manguito-cms-core": "0.2.0",
|
|
33
|
+
"@bobbykim/manguito-cms-admin": "0.3.0",
|
|
34
|
+
"@bobbykim/manguito-cms-db": "0.1.2",
|
|
35
|
+
"@bobbykim/manguito-cms-api": "0.2.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^25.9.5",
|