@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.
Files changed (2) hide show
  1. package/dist/index.js +365 -95
  2. 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(resolve9, reject) {
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
- resolve9(res);
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(resolve9, reject) {
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
- resolve9(res);
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(resolve9, reject) {
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
- resolve9(res);
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((resolve9) => stream.on("end", () => resolve9()));
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((resolve9, reject) => {
13115
+ return new this._Promise((resolve10, reject) => {
13116
13116
  this._connect((error51) => {
13117
13117
  if (error51) {
13118
13118
  reject(error51);
13119
13119
  } else {
13120
- resolve9(this);
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((resolve9, reject) => {
13468
- query.callback = (err, res) => err ? reject(err) : resolve9(res);
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((resolve9) => {
13553
- this.connection.once("end", resolve9);
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(resolve9, reject) {
13604
- res = resolve9;
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((resolve9) => resolve9(f()));
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(resolve9, reject) {
14060
- this._once("end", resolve9);
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((resolve9, reject) => {
14239
+ return new this._Promise((resolve10, reject) => {
14240
14240
  this._connect((error51) => {
14241
14241
  if (error51) {
14242
14242
  reject(error51);
14243
14243
  } else {
14244
- resolve9(this);
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((resolve9, reject) => {
14269
- resolveOut = resolve9;
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(resolve9, reject) {
14333
- cb = (err) => err ? reject(err) : resolve9();
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 join6, dirname } from "path";
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, required: required2 } = rawField;
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 join11(chunks, separator) {
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 = join11;
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((join11) => join11.alias === tableName)) {
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((join11) => join11.alias === tableName)) {
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 join11 of this.config.joins) {
36236
- const tableName2 = getTableLikeName(join11.table);
36237
- if (typeof tableName2 === "string" && !is(join11.table, SQL)) {
36238
- const fromFields = this.getTableLikeFields(join11.table);
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(join6(generatedDir, "schema.ts"), generateSchemaFile(registry2), "utf8");
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, join6(generatedDir, "forms"));
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(join6(cwd, "package.json"));
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 = join6(dir, entry.name);
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 { resolve as resolve5, join as join9, dirname as dirname2, extname as extname2 } from "path";
44767
- import { mkdir as mkdir2, writeFile as writeFile7, watch, readFile } from "fs/promises";
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
- return c.json({ ok: true, data: result.data[0] });
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, schemaFieldNames);
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
- return c.json({ ok: true, data: item });
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
- return c.json({ ok: true, data: item });
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.1.1";
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 writeFile5 } from "fs/promises";
50754
- import { join as join7, relative, resolve as resolve4 } from "path";
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 = relative(resolve4(targetDir), resolve4(migrationsFolder)).replace(/\\/g, "/");
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 writeFile5(join7(targetDir, "drizzle.config.ts"), content, "utf8");
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 writeFile6 } from "fs/promises";
50782
- import { join as join8 } from "path";
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 writeFile6(join8(targetDir, "nav.ts"), content, "utf8");
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 = resolve5(cwd, ".manguito");
51102
+ const manguitoDir = resolve6(cwd, ".manguito");
50859
51103
  await mkdir2(manguitoDir, { recursive: true });
50860
- const drizzleConfigPath = resolve5(manguitoDir, "drizzle.config.ts");
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 writeFile7(join9(manguitoDir, "schema.ts"), generateSchemaFile(bootstrapRegistry), "utf8");
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, join9(manguitoDir, "forms"));
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 = resolve5(cwd, "uploads");
50949
- const filePath = resolve5(uploadsDir, relativePath);
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 = extname2(filePath).slice(1).toLowerCase();
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 = resolve5(cwd, config3.schema.base_path);
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 writeFile7(join9(manguitoDir, "schema.ts"), generateSchemaFile(registry2), "utf8");
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, join9(manguitoDir, "forms"));
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 = resolve5(cwd, config3.schema.base_path, "roles.json");
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 = resolve5(cwd, config3.schema.base_path, "routes.json");
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(join9(cwd, "package.json"));
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 resolve6 } from "path";
51131
- import { pathToFileURL as pathToFileURL2 } from "url";
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 ? resolve6(cwd, config3.migrations.folder) : resolve6(cwd, "./migrations");
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 = resolve6(cwd, config3.schema.base_path, "roles.json");
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 = resolve6(cwd, config3.schema.base_path, "routes.json");
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 = pathToFileURL2(resolve6(cwd, "dist/server.js")).href;
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 resolve7, join as join10 } from "path";
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: resolve7(cwd, "./migrations"),
51241
- configPath: resolve7(cwd, "dist/generated/drizzle.config.ts"),
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 ? resolve7(process.cwd(), config4.migrations.folder) : deps.migrationsFolder;
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 ? resolve7(cwd, config3.migrations.folder) : deps.migrationsFolder;
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 = resolve7(cwd, "dist/generated");
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) => join10(migrationsFolder, 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 = resolve7(cwd, config3.schema.base_path, "roles.json");
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 = resolve7(cwd, config3.schema.base_path, "routes.json");
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 resolve8 } from "path";
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 = resolve8(cwd, config3.schema.base_path, "roles.json");
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 = resolve8(cwd, config3.schema.base_path, "routes.json");
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.2.2",
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-admin": "0.2.0",
33
- "@bobbykim/manguito-cms-api": "0.1.1",
34
- "@bobbykim/manguito-cms-db": "0.1.1",
35
- "@bobbykim/manguito-cms-core": "0.1.1"
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",