@bobbykim/manguito-cms-cli 0.2.1 → 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 +375 -97
  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,10 +44814,11 @@ 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
- import { createServer as createViteServer } from "vite";
44821
+ import { createServer as createViteServer, searchForWorkspaceRoot } from "vite";
44770
44822
 
44771
44823
  // ../api/dist/index.mjs
44772
44824
  init_esm_shims();
@@ -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 } : {},
@@ -50922,7 +51168,15 @@ async function runDev(options, deps) {
50922
51168
  let honoFetch = (req) => adapter.app.fetch(req);
50923
51169
  const vite = await createViteServer({
50924
51170
  root: resolveAdminRoot2(cwd),
50925
- server: { middlewareMode: true },
51171
+ server: {
51172
+ middlewareMode: true,
51173
+ // The admin package (Vite root) lives deep in node_modules/.pnpm, so Vite's
51174
+ // default file-serving allow list does not cover sibling deps like the
51175
+ // @fontsource font files. Allow the project root (covers cwd/node_modules
51176
+ // in installed projects) and the workspace root (covers hoisted deps +
51177
+ // packages/admin in the monorepo).
51178
+ fs: { allow: [searchForWorkspaceRoot(cwd), cwd] }
51179
+ },
50926
51180
  appType: "spa",
50927
51181
  logLevel: "warn"
50928
51182
  });
@@ -50937,8 +51191,8 @@ async function runDev(options, deps) {
50937
51191
  }
50938
51192
  if (url2.startsWith("/uploads/")) {
50939
51193
  const relativePath = (url2.slice("/uploads/".length).split("?")[0] ?? "").replace(/\.\./g, "");
50940
- const uploadsDir = resolve5(cwd, "uploads");
50941
- const filePath = resolve5(uploadsDir, relativePath);
51194
+ const uploadsDir = resolve6(cwd, "uploads");
51195
+ const filePath = resolve6(uploadsDir, relativePath);
50942
51196
  if (!filePath.startsWith(uploadsDir + "/") && filePath !== uploadsDir) {
50943
51197
  res.statusCode = 403;
50944
51198
  res.end("Forbidden");
@@ -50946,7 +51200,7 @@ async function runDev(options, deps) {
50946
51200
  }
50947
51201
  try {
50948
51202
  const data = await readFile(filePath);
50949
- const ext = extname2(filePath).slice(1).toLowerCase();
51203
+ const ext = extname3(filePath).slice(1).toLowerCase();
50950
51204
  const mime = UPLOAD_MIME_MAP[ext] ?? "application/octet-stream";
50951
51205
  res.setHeader("Content-Type", mime);
50952
51206
  res.setHeader("X-Content-Type-Options", "nosniff");
@@ -50979,7 +51233,7 @@ async function runDev(options, deps) {
50979
51233
  printSuccess(`Dev server running at http://localhost:${port}`);
50980
51234
  printSuccess(`Admin panel at http://localhost:${port}${config3.admin.prefix}`);
50981
51235
  });
50982
- const schemasDir = resolve5(cwd, config3.schema.base_path);
51236
+ const schemasDir = resolve6(cwd, config3.schema.base_path);
50983
51237
  const watcher = watch(schemasDir, { recursive: true });
50984
51238
  void (async () => {
50985
51239
  for await (const event of watcher) {
@@ -50992,14 +51246,36 @@ async function runDev(options, deps) {
50992
51246
  db,
50993
51247
  updateFetch: (fetch2) => {
50994
51248
  honoFetch = fetch2;
50995
- }
51249
+ },
51250
+ getResolvers: () => loadProgrammaticResolvers(cwd, config3.programmatic.dir)
50996
51251
  });
50997
51252
  }
50998
51253
  }
50999
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
+ }
51000
51276
  }
51001
51277
  async function onSchemaFileChange(args) {
51002
- const { cwd, config: config3, manguitoDir, drizzleConfigPath, db, updateFetch } = args;
51278
+ const { cwd, config: config3, manguitoDir, drizzleConfigPath, db, updateFetch, getResolvers } = args;
51003
51279
  let registry2;
51004
51280
  try {
51005
51281
  registry2 = await parseAllSchemas(cwd, config3);
@@ -51008,10 +51284,10 @@ async function onSchemaFileChange(args) {
51008
51284
  return;
51009
51285
  }
51010
51286
  await generateDrizzleConfig(config3, manguitoDir);
51011
- await writeFile7(join9(manguitoDir, "schema.ts"), generateSchemaFile(registry2), "utf8");
51287
+ await writeFile8(join11(manguitoDir, "schema.ts"), generateSchemaFile(registry2), "utf8");
51012
51288
  await generateSchemaRegistry(registry2, manguitoDir);
51013
51289
  await generateRoutes2(registry2, manguitoDir);
51014
- await generateForms(registry2, join9(manguitoDir, "forms"));
51290
+ await generateForms(registry2, join11(manguitoDir, "forms"));
51015
51291
  await generateNav(registry2, manguitoDir);
51016
51292
  try {
51017
51293
  await runDevMigration(drizzleConfigPath);
@@ -51020,11 +51296,13 @@ async function onSchemaFileChange(args) {
51020
51296
  `);
51021
51297
  return;
51022
51298
  }
51299
+ const resolverMap = await getResolvers();
51023
51300
  const newAdapter = createCmsApp({
51024
51301
  name: config3.name,
51025
51302
  registry: registry2,
51026
51303
  db: db.getDb(),
51027
51304
  storage: config3.storage,
51305
+ resolvers: resolverMap,
51028
51306
  ...config3.api.prefix ? { prefix: config3.api.prefix } : {},
51029
51307
  ...config3.api.media?.max_file_size ? { media: { max_file_size: config3.api.media.max_file_size } } : {},
51030
51308
  ...config3.api.rateLimit ? { rateLimit: config3.api.rateLimit } : {},
@@ -51048,7 +51326,7 @@ async function parseAllSchemas(cwd, config3) {
51048
51326
  }
51049
51327
  parsedSchemas.push(parseResult.schema);
51050
51328
  }
51051
- const rolesPath = resolve5(cwd, config3.schema.base_path, "roles.json");
51329
+ const rolesPath = resolve6(cwd, config3.schema.base_path, "roles.json");
51052
51330
  const rolesLoad = loadSchemaFile(rolesPath);
51053
51331
  if (!rolesLoad.ok) {
51054
51332
  printGuidedError("Could not read roles.json.");
@@ -51059,7 +51337,7 @@ async function parseAllSchemas(cwd, config3) {
51059
51337
  printGuidedError("roles.json parse errors \u2014 run `manguito validate` for details.");
51060
51338
  process.exit(1);
51061
51339
  }
51062
- const routesPath = resolve5(cwd, config3.schema.base_path, "routes.json");
51340
+ const routesPath = resolve6(cwd, config3.schema.base_path, "routes.json");
51063
51341
  const routesLoad = loadSchemaFile(routesPath);
51064
51342
  if (!routesLoad.ok) {
51065
51343
  printGuidedError("Could not read routes.json.");
@@ -51073,7 +51351,7 @@ async function parseAllSchemas(cwd, config3) {
51073
51351
  return buildSchemaRegistry(parsedSchemas, routesResult.value, rolesResult.value);
51074
51352
  }
51075
51353
  function resolveAdminRoot2(cwd) {
51076
- const cwdRequire = createRequire2(join9(cwd, "package.json"));
51354
+ const cwdRequire = createRequire2(join11(cwd, "package.json"));
51077
51355
  const adminPkgJson = cwdRequire.resolve("@bobbykim/manguito-cms-admin/package.json");
51078
51356
  return dirname2(adminPkgJson);
51079
51357
  }
@@ -51119,8 +51397,8 @@ async function bridgeToHono(req, res, honoFetch) {
51119
51397
 
51120
51398
  // src/commands/start.ts
51121
51399
  init_esm_shims();
51122
- import { resolve as resolve6 } from "path";
51123
- import { pathToFileURL as pathToFileURL2 } from "url";
51400
+ import { resolve as resolve7 } from "path";
51401
+ import { pathToFileURL as pathToFileURL3 } from "url";
51124
51402
  function registerStart(program2) {
51125
51403
  program2.command("start").description("Start the production server from dist/").option("--env <path>", "path to .env file to load").action(async (options) => {
51126
51404
  await runStart(options, { cwd: process.cwd() });
@@ -51132,7 +51410,7 @@ async function runStart(options, deps) {
51132
51410
  const config3 = await resolveConfig(cwd);
51133
51411
  const db = await connectDb(config3);
51134
51412
  const migrationsTable = config3.migrations?.table ?? "__manguito_migrations";
51135
- 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");
51136
51414
  const trackingTableExists = await db.tableExists(migrationsTable);
51137
51415
  if (!trackingTableExists) {
51138
51416
  printGuidedError(
@@ -51167,7 +51445,7 @@ async function runStart(options, deps) {
51167
51445
  } else {
51168
51446
  allErrors.push(...walkResult.errors);
51169
51447
  }
51170
- const rolesPath = resolve6(cwd, config3.schema.base_path, "roles.json");
51448
+ const rolesPath = resolve7(cwd, config3.schema.base_path, "roles.json");
51171
51449
  const rolesLoad = loadSchemaFile(rolesPath);
51172
51450
  let parsedRoles = null;
51173
51451
  if (rolesLoad.ok) {
@@ -51180,7 +51458,7 @@ async function runStart(options, deps) {
51180
51458
  } else {
51181
51459
  allErrors.push(...rolesLoad.errors);
51182
51460
  }
51183
- const routesPath = resolve6(cwd, config3.schema.base_path, "routes.json");
51461
+ const routesPath = resolve7(cwd, config3.schema.base_path, "routes.json");
51184
51462
  const routesLoad = loadSchemaFile(routesPath);
51185
51463
  let parsedRoutes = null;
51186
51464
  if (routesLoad.ok) {
@@ -51202,7 +51480,7 @@ async function runStart(options, deps) {
51202
51480
  }
51203
51481
  const registry2 = buildSchemaRegistry(parsedSchemas, parsedRoutes, parsedRoles);
51204
51482
  await seedSystemTables(db.getDb(), registry2);
51205
- const serverUrl = pathToFileURL2(resolve6(cwd, "dist/server.js")).href;
51483
+ const serverUrl = pathToFileURL3(resolve7(cwd, "dist/server.js")).href;
51206
51484
  await (deps.loadServer ? deps.loadServer(serverUrl) : import(serverUrl));
51207
51485
  const port = Number(process.env["PORT"] ?? 3e3);
51208
51486
  printSuccess(`Server running at http://localhost:${port}`);
@@ -51211,7 +51489,7 @@ async function runStart(options, deps) {
51211
51489
 
51212
51490
  // src/commands/migrate.ts
51213
51491
  init_esm_shims();
51214
- import { resolve as resolve7, join as join10 } from "path";
51492
+ import { resolve as resolve8, join as join12 } from "path";
51215
51493
  import { readFile as readFile2 } from "fs/promises";
51216
51494
  function registerMigrate(program2) {
51217
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) => {
@@ -51229,8 +51507,8 @@ function buildDeps(options, cwd) {
51229
51507
  buildRunner: async () => runBuild(options.env ? { env: options.env } : {}, { cwd }),
51230
51508
  needsRebuild: () => needsRebuild(cwd),
51231
51509
  db: createPostgresAdapter(),
51232
- migrationsFolder: resolve7(cwd, "./migrations"),
51233
- configPath: resolve7(cwd, "dist/generated/drizzle.config.ts"),
51510
+ migrationsFolder: resolve8(cwd, "./migrations"),
51511
+ configPath: resolve8(cwd, "dist/generated/drizzle.config.ts"),
51234
51512
  prompt: createPromptAdapter()
51235
51513
  };
51236
51514
  }
@@ -51245,7 +51523,7 @@ async function runMigrate(options, deps) {
51245
51523
  }
51246
51524
  const config4 = await resolveConfig(process.cwd());
51247
51525
  const migrationsTable2 = config4.migrations?.table ?? "__manguito_migrations";
51248
- 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;
51249
51527
  const status = await getMigrationStatus(deps.db.getDb(), { migrationsTable: migrationsTable2, migrationsFolder: migrationsFolder2 });
51250
51528
  if (status.applied.length === 0 && status.pending.length === 0) {
51251
51529
  process.stdout.write(
@@ -51277,7 +51555,7 @@ async function runMigrate(options, deps) {
51277
51555
  loadEnvFile(options.env);
51278
51556
  const cwd = process.cwd();
51279
51557
  const config3 = await resolveConfig(cwd);
51280
- 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;
51281
51559
  const migrationsTable = config3.migrations?.table ?? "__manguito_migrations";
51282
51560
  try {
51283
51561
  await deps.db.connect();
@@ -51297,10 +51575,10 @@ async function runMigrate(options, deps) {
51297
51575
  }
51298
51576
  }
51299
51577
  printSuccess("Build complete");
51300
- const generatedDir = resolve7(cwd, "dist/generated");
51578
+ const generatedDir = resolve8(cwd, "dist/generated");
51301
51579
  await generateDrizzleConfig(config3, generatedDir);
51302
51580
  const newFileNames = await generateMigration(deps.configPath, migrationsFolder);
51303
- const newFilePaths = newFileNames.map((f) => join10(migrationsFolder, f));
51581
+ const newFilePaths = newFileNames.map((f) => join12(migrationsFolder, f));
51304
51582
  printSuccess(
51305
51583
  newFileNames.length === 0 ? "No new migration files generated" : `Generated ${newFileNames.length} migration file${newFileNames.length !== 1 ? "s" : ""}: ${newFileNames.join(", ")}`
51306
51584
  );
@@ -51370,7 +51648,7 @@ async function runMigrate(options, deps) {
51370
51648
  } else {
51371
51649
  allErrors.push(...walkResult.errors);
51372
51650
  }
51373
- const rolesPath = resolve7(cwd, config3.schema.base_path, "roles.json");
51651
+ const rolesPath = resolve8(cwd, config3.schema.base_path, "roles.json");
51374
51652
  const rolesLoad = loadSchemaFile(rolesPath);
51375
51653
  let parsedRoles = null;
51376
51654
  if (rolesLoad.ok) {
@@ -51383,7 +51661,7 @@ async function runMigrate(options, deps) {
51383
51661
  } else {
51384
51662
  allErrors.push(...rolesLoad.errors);
51385
51663
  }
51386
- const routesPath = resolve7(cwd, config3.schema.base_path, "routes.json");
51664
+ const routesPath = resolve8(cwd, config3.schema.base_path, "routes.json");
51387
51665
  const routesLoad = loadSchemaFile(routesPath);
51388
51666
  let parsedRoutes = null;
51389
51667
  if (routesLoad.ok) {
@@ -51415,7 +51693,7 @@ async function runMigrate(options, deps) {
51415
51693
 
51416
51694
  // src/commands/validate.ts
51417
51695
  init_esm_shims();
51418
- import { resolve as resolve8 } from "path";
51696
+ import { resolve as resolve9 } from "path";
51419
51697
  function registerValidate(program2) {
51420
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) => {
51421
51699
  await runValidate(options, { cwd: process.cwd() });
@@ -51440,7 +51718,7 @@ async function runValidate(options, deps) {
51440
51718
  }
51441
51719
  }
51442
51720
  }
51443
- const rolesPath = resolve8(cwd, config3.schema.base_path, "roles.json");
51721
+ const rolesPath = resolve9(cwd, config3.schema.base_path, "roles.json");
51444
51722
  const rolesLoadResult = loadSchemaFile(rolesPath);
51445
51723
  let parsedRoles = null;
51446
51724
  if (!rolesLoadResult.ok) {
@@ -51453,7 +51731,7 @@ async function runValidate(options, deps) {
51453
51731
  parsedRoles = rolesParseResult.value;
51454
51732
  }
51455
51733
  }
51456
- const routesPath = resolve8(cwd, config3.schema.base_path, "routes.json");
51734
+ const routesPath = resolve9(cwd, config3.schema.base_path, "routes.json");
51457
51735
  const routesLoadResult = loadSchemaFile(routesPath);
51458
51736
  let parsedRoutesDef = null;
51459
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.1",
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-core": "0.1.1",
34
- "@bobbykim/manguito-cms-db": "0.1.1",
35
- "@bobbykim/manguito-cms-api": "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",