betterstart-cli 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3867,6 +3867,61 @@ function generateFieldMapping(field, source = "input") {
3867
3867
  function getFieldType(field, mode = "output") {
3868
3868
  return toTypeScriptType(field, mode);
3869
3869
  }
3870
+ function getReadFieldType(field) {
3871
+ if (field.type === "select") return "SelectOptionValue[]";
3872
+ if (field.type === "group" && field.fields && field.fields.length > 0) {
3873
+ const groupType = field.fields.map(
3874
+ (nestedField) => `${quotePropertyName(nestedField.name)}?: ${getReadFieldType(nestedField)}`
3875
+ ).join("; ");
3876
+ return `{ ${groupType} }`;
3877
+ }
3878
+ if (field.type === "list" && field.fields && field.fields.length > 0) {
3879
+ const nestedType = field.fields.flatMap((nestedField) => {
3880
+ const fields = [`${quotePropertyName(nestedField.name)}?: ${getReadFieldType(nestedField)}`];
3881
+ if (nestedField.hasIcon) {
3882
+ fields.push(`${quotePropertyName(`${nestedField.name}Icon`)}?: string`);
3883
+ }
3884
+ return fields;
3885
+ }).join("; ");
3886
+ return `Array<{ ${nestedType} }>`;
3887
+ }
3888
+ return getFieldType(field, "output");
3889
+ }
3890
+ function collectReadSelectFields(fields, path52 = []) {
3891
+ const matches = fields.flatMap((field) => {
3892
+ const currentPath = [...path52, field.name];
3893
+ const matches2 = field.type === "select" ? [{ field, path: currentPath }] : [];
3894
+ if (field.fields) {
3895
+ matches2.push(...collectReadSelectFields(field.fields, currentPath));
3896
+ }
3897
+ if (field.tabs) {
3898
+ for (const tab of field.tabs) {
3899
+ matches2.push(...collectReadSelectFields(tab.fields ?? [], currentPath));
3900
+ if (tab.slot?.main?.fields) {
3901
+ matches2.push(...collectReadSelectFields(tab.slot.main.fields, currentPath));
3902
+ }
3903
+ if (tab.slot?.sidebar?.fields) {
3904
+ matches2.push(...collectReadSelectFields(tab.slot.sidebar.fields, currentPath));
3905
+ }
3906
+ }
3907
+ }
3908
+ return matches2;
3909
+ });
3910
+ const seen = /* @__PURE__ */ new Set();
3911
+ return matches.filter((match) => {
3912
+ const key = match.path.join(".");
3913
+ if (seen.has(key)) return false;
3914
+ seen.add(key);
3915
+ return true;
3916
+ });
3917
+ }
3918
+ function buildReadSelectOptionType(selectFields) {
3919
+ if (selectFields.length === 0) return null;
3920
+ return `export interface SelectOptionValue {
3921
+ label: string
3922
+ value: string
3923
+ }`;
3924
+ }
3870
3925
  function buildRelationshipSelect(allDbFields, relationshipFields, tableVar, htmlFields = [], extraSelects = [], extraJoins = "") {
3871
3926
  const fieldSelects = allDbFields.filter((f) => f.type !== "relationship").map((f) => ` ${f.name}: ${tableVar}.${f.name}`);
3872
3927
  const htmlSelects = htmlFields.map((f) => ` ${f.name}Html: ${tableVar}.${f.name}Html`);
@@ -3892,12 +3947,17 @@ function buildResultMapping(allDbFields, relationshipFields, Plural, camelPlural
3892
3947
  ` const createdByUser = row.createdByUser as { id?: string | null; name?: string | null; email?: string | null } | null | undefined`,
3893
3948
  ` const updatedByUser = row.updatedByUser as { id?: string | null; name?: string | null; email?: string | null } | null | undefined`
3894
3949
  ].filter(Boolean).join("\n");
3950
+ const mappedTimestampFields = allDbFields.some((f) => f.name === "createdAtFormatted") ? [] : [
3951
+ ` createdAtFormatted: formatActionDate(row.createdAt)`,
3952
+ ` updatedAtFormatted: formatActionDate(row.updatedAt)`
3953
+ ];
3895
3954
  const rowMappings = allDbFields.map((f) => {
3896
3955
  if (f.type === "relationship" && !f.multiple) {
3897
3956
  return ` ${f.name}: ${f.name}?.id ? { id: ${f.name}.id, name: ${f.name}.name ?? undefined, title: ${f.name}.title ?? undefined, slug: ${f.name}.slug ?? undefined, label: ${f.name}.label ?? undefined } : null`;
3898
3957
  }
3899
3958
  return ` ${f.name}: row.${f.name}`;
3900
3959
  }).concat([
3960
+ ...mappedTimestampFields,
3901
3961
  ` createdByUser: createdByUser?.id ? { id: createdByUser.id, name: createdByUser.name ?? '', email: createdByUser.email ?? '' } : null`,
3902
3962
  ` updatedByUser: updatedByUser?.id ? { id: updatedByUser.id, name: updatedByUser.name ?? '', email: updatedByUser.email ?? '' } : null`
3903
3963
  ]).join(",\n");
@@ -3926,7 +3986,7 @@ ${rowMappings}
3926
3986
  return `${mapBlock}
3927
3987
 
3928
3988
  return {
3929
- ${camelPlural}: mapped${Plural} as ${Singular}[],
3989
+ ${camelPlural}: mapped${Plural} as unknown as ${Singular}[],
3930
3990
  total
3931
3991
  }`;
3932
3992
  }
@@ -3938,15 +3998,20 @@ function buildSingleRowMapping(allDbFields, _relationshipFields, typeName, htmlF
3938
3998
  return ` ${f.name}: row.${f.name}`;
3939
3999
  });
3940
4000
  const htmlMappings = htmlFields.map((f) => ` ${f.name}Html: row.${f.name}Html`);
4001
+ const singleTimestampFields = allDbFields.some((f) => f.name === "createdAtFormatted") ? [] : [
4002
+ ` createdAtFormatted: formatActionDate(row.createdAt)`,
4003
+ ` updatedAtFormatted: formatActionDate(row.updatedAt)`
4004
+ ];
3941
4005
  const mappings = [
3942
4006
  ...fieldMappings,
3943
4007
  ...htmlMappings,
4008
+ ...singleTimestampFields,
3944
4009
  ` createdByUser: row.createdByUser?.id ? { id: row.createdByUser.id, name: row.createdByUser.name, email: row.createdByUser.email } : null`,
3945
4010
  ` updatedByUser: row.updatedByUser?.id ? { id: row.updatedByUser.id, name: row.updatedByUser.name, email: row.updatedByUser.email } : null`
3946
4011
  ].join(",\n");
3947
4012
  return `{
3948
4013
  ${mappings}
3949
- } as ${typeName}`;
4014
+ } as unknown as ${typeName}`;
3950
4015
  }
3951
4016
  function buildExplicitSelect(fields, tableVar, htmlFields = [], extraSelects = [], extraJoins = "") {
3952
4017
  const fieldSelects = fields.map((f) => ` ${f.name}: ${tableVar}.${f.name}`);
@@ -4172,6 +4237,7 @@ var SINGLE_ACTION_FIELD_EXCLUSIONS = /* @__PURE__ */ new Set(["id", "createdAt",
4172
4237
  var DEFAULT_ADMIN_IMPORT_ALIAS = "@admin";
4173
4238
  var SAMPLE_UUID = "00000000-0000-4000-8000-000000000001";
4174
4239
  var SINGLETON_ID = "00000000-0000-0000-0000-000000000001";
4240
+ var SAMPLE_FORMATTED_TIMESTAMP = "Jan 01, 2026";
4175
4241
  function isActionField(field, exclusions) {
4176
4242
  return !field.primaryKey && !exclusions.has(field.name) && !isAuthorshipFieldName(field.name) && !isLayoutField(field.type);
4177
4243
  }
@@ -4354,6 +4420,8 @@ function sampleAdminMedia() {
4354
4420
  tags: ["example"],
4355
4421
  createdAt: "2026-01-01T00:00:00.000Z",
4356
4422
  updatedAt: "2026-01-01T00:00:00.000Z",
4423
+ createdAtFormatted: SAMPLE_FORMATTED_TIMESTAMP,
4424
+ updatedAtFormatted: SAMPLE_FORMATTED_TIMESTAMP,
4357
4425
  createdBy: "user-id",
4358
4426
  updatedBy: "user-id"
4359
4427
  };
@@ -4369,6 +4437,14 @@ function sampleSchemaOutputValue(field) {
4369
4437
  if (field.name === "id") return SAMPLE_UUID;
4370
4438
  if (field.name === "createdAt" || field.name === "updatedAt") return "2026-01-01T00:00:00.000Z";
4371
4439
  if (field.name === "createdBy" || field.name === "updatedBy") return "user-id";
4440
+ if (field.type === "select") {
4441
+ const sampleValue = sampleSchemaFieldValue(field);
4442
+ const values = Array.isArray(sampleValue) ? sampleValue : sampleValue ? [sampleValue] : [];
4443
+ return values.filter((value) => typeof value === "string" && value.length > 0).map((value) => ({
4444
+ label: field.options?.find((option) => option.value === value)?.label ?? value,
4445
+ value
4446
+ }));
4447
+ }
4372
4448
  if (field.type === "relationship" && field.relationship && !field.multiple) {
4373
4449
  return {
4374
4450
  id: SAMPLE_UUID,
@@ -4378,6 +4454,18 @@ function sampleSchemaOutputValue(field) {
4378
4454
  label: "Example related record"
4379
4455
  };
4380
4456
  }
4457
+ if (field.type === "group" && field.fields && field.fields.length > 0) {
4458
+ return Object.fromEntries(
4459
+ field.fields.filter((nestedField) => nestedField.name && !isLayoutField(nestedField.type)).map((nestedField) => [nestedField.name, sampleSchemaOutputValue(nestedField)])
4460
+ );
4461
+ }
4462
+ if (field.type === "list" && field.fields && field.fields.length > 0) {
4463
+ return [
4464
+ Object.fromEntries(
4465
+ field.fields.filter((nestedField) => nestedField.name && !isLayoutField(nestedField.type)).map((nestedField) => [nestedField.name, sampleSchemaOutputValue(nestedField)])
4466
+ )
4467
+ ];
4468
+ }
4381
4469
  return sampleSchemaFieldValue(field);
4382
4470
  }
4383
4471
  function sampleSingleOutputValue(field) {
@@ -4392,7 +4480,7 @@ function createEntityDataObject(schema) {
4392
4480
  return field.type === "image" || field.type === "video" || field.type === "media";
4393
4481
  });
4394
4482
  const galleryFields = regularDbFields.filter((field) => field.type === "gallery");
4395
- return {
4483
+ return addFormattedTimestamps({
4396
4484
  ...Object.fromEntries(allDbFields.map((field) => [field.name, sampleSchemaOutputValue(field)])),
4397
4485
  ...Object.fromEntries(
4398
4486
  htmlOutputFields.map((field) => [
@@ -4407,7 +4495,17 @@ function createEntityDataObject(schema) {
4407
4495
  ),
4408
4496
  createdByUser: sampleAdminUserSummary(),
4409
4497
  updatedByUser: sampleAdminUserSummary()
4410
- };
4498
+ });
4499
+ }
4500
+ function addFormattedTimestamps(data) {
4501
+ const output = { ...data };
4502
+ if (typeof output.createdAt === "string") {
4503
+ output.createdAtFormatted = SAMPLE_FORMATTED_TIMESTAMP;
4504
+ }
4505
+ if (typeof output.updatedAt === "string") {
4506
+ output.updatedAtFormatted = SAMPLE_FORMATTED_TIMESTAMP;
4507
+ }
4508
+ return output;
4411
4509
  }
4412
4510
  function getSingleAllDbFields(schema) {
4413
4511
  const dbFields = flattenFields(schema.fields).filter(
@@ -4430,7 +4528,7 @@ function getSingleAllDbFields(schema) {
4430
4528
  function createSingleDataObject(schema) {
4431
4529
  const dbFields = getSingleAllDbFields(schema);
4432
4530
  const htmlOutputFields = getHtmlOutputFields(dbFields);
4433
- return {
4531
+ return addFormattedTimestamps({
4434
4532
  ...Object.fromEntries(dbFields.map((field) => [field.name, sampleSingleOutputValue(field)])),
4435
4533
  ...Object.fromEntries(
4436
4534
  htmlOutputFields.map((field) => [
@@ -4440,7 +4538,7 @@ function createSingleDataObject(schema) {
4440
4538
  ),
4441
4539
  createdByUser: sampleAdminUserSummary(),
4442
4540
  updatedByUser: sampleAdminUserSummary()
4443
- };
4541
+ });
4444
4542
  }
4445
4543
  function createFormSubmissionObject(fields) {
4446
4544
  return {
@@ -8007,7 +8105,10 @@ function genHelpersContent(ctx) {
8007
8105
  drizzleImports.add("inArray");
8008
8106
  schemaImports.add("adminMedia");
8009
8107
  }
8010
- if (ctx.hasM2M || ctx.hasListRels || ctx.hasMediaFields) {
8108
+ if (ctx.hasCreatableSelectFields) {
8109
+ schemaImports.add(ctx.selectOptionsTableVar);
8110
+ }
8111
+ if (ctx.hasM2M || ctx.hasListRels || ctx.hasMediaFields || ctx.hasCreatableSelectFields) {
8011
8112
  lines.push(`import db from '@admin/db'`);
8012
8113
  }
8013
8114
  const tables = [...schemaImports].sort();
@@ -8020,7 +8121,21 @@ function genHelpersContent(ctx) {
8020
8121
  if (ctx.hasMediaFields) {
8021
8122
  lines.push(`import type { AdminMedia } from '@admin/types'`);
8022
8123
  }
8023
- lines.push(`import type { ${ctx.Singular} } from './types'`);
8124
+ const typeImports = ctx.hasSelectFields ? `${ctx.Singular}, SelectOptionValue` : ctx.Singular;
8125
+ lines.push(`import type { ${typeImports} } from './types'`);
8126
+ lines.push("");
8127
+ lines.push(`export function formatActionDate(value: unknown): string {
8128
+ if (typeof value !== 'string') return ''
8129
+ const date = new Date(value)
8130
+ if (Number.isNaN(date.getTime())) return ''
8131
+
8132
+ return date.toLocaleDateString('en-US', {
8133
+ month: 'short',
8134
+ day: '2-digit',
8135
+ year: 'numeric',
8136
+ timeZone: 'UTC'
8137
+ })
8138
+ }`);
8024
8139
  lines.push("");
8025
8140
  if (ctx.hasListRels) {
8026
8141
  const exportedFn = ctx.populateListRelsFn.replace("async function", "export async function").trim();
@@ -8031,6 +8146,10 @@ function genHelpersContent(ctx) {
8031
8146
  lines.push(generateResolveMediaFunction(ctx));
8032
8147
  lines.push("");
8033
8148
  }
8149
+ if (ctx.hasSelectFields) {
8150
+ lines.push(generateResolveSelectFieldsFunction(ctx));
8151
+ lines.push("");
8152
+ }
8034
8153
  lines.push(generateVersionSnapshotHelpers(ctx));
8035
8154
  lines.push("");
8036
8155
  return lines.join("\n");
@@ -8081,7 +8200,7 @@ const VERSION_RESTORE_KEYS = [${restoreKeys}] as const
8081
8200
  const VERSION_M2M_KEYS = [${m2mKeys}] as const
8082
8201
 
8083
8202
  export function build${ctx.Singular}VersionSnapshot(row: Partial<${ctx.Singular}> | Record<string, unknown>): Record<string, unknown> {
8084
- const source = row as Record<string, unknown>
8203
+ const source = row as unknown as Record<string, unknown>
8085
8204
  const snapshot: Record<string, unknown> = {}
8086
8205
 
8087
8206
  for (const key of VERSION_SNAPSHOT_KEYS) {
@@ -8133,10 +8252,32 @@ function generateResolveMediaFunction(ctx) {
8133
8252
  }`
8134
8253
  ).join("\n");
8135
8254
  const assignSingle = singleFields.map(
8136
- (name) => ` row.${name}Media = (row.${name} && !row.${name}.startsWith('http')) ? mediaMap.get(row.${name}) ?? null : null`
8255
+ (name) => ` row.${name}Media = row.${name} && !row.${name}.startsWith('http')
8256
+ ? (() => {
8257
+ const media = mediaMap.get(row.${name})
8258
+ if (!media) return null
8259
+ return {
8260
+ ...media,
8261
+ createdAtFormatted: formatActionDate(media.createdAt),
8262
+ updatedAtFormatted: formatActionDate(media.updatedAt)
8263
+ }
8264
+ })()
8265
+ : null`
8137
8266
  ).join("\n");
8138
8267
  const assignArray = arrayFields.map(
8139
- (name) => ` row.${name}Media = Array.isArray(row.${name}) ? row.${name}.map((id: string) => mediaMap.get(id)).filter(Boolean) as AdminMedia[] : []`
8268
+ (name) => ` row.${name}Media = Array.isArray(row.${name})
8269
+ ? row.${name}
8270
+ .map((id: string) => {
8271
+ const media = mediaMap.get(id)
8272
+ if (!media) return null
8273
+ return {
8274
+ ...media,
8275
+ createdAtFormatted: formatActionDate(media.createdAt),
8276
+ updatedAtFormatted: formatActionDate(media.updatedAt)
8277
+ }
8278
+ })
8279
+ .filter(Boolean) as AdminMedia[]
8280
+ : []`
8140
8281
  ).join("\n");
8141
8282
  return `export async function resolve${ctx.Singular}MediaFields(rows: ${ctx.Singular}[]): Promise<${ctx.Singular}[]> {
8142
8283
  if (rows.length === 0) return rows
@@ -8164,6 +8305,95 @@ ${assignArray}` : ""}
8164
8305
  return rows
8165
8306
  }`;
8166
8307
  }
8308
+ function generateResolveSelectFieldsFunction(ctx) {
8309
+ const staticLabelMaps = ctx.selectFields.map((readField) => {
8310
+ const key = readField.path.join(".");
8311
+ const entries = (readField.field.options ?? []).map((option) => `${JSON.stringify(option.value)}: ${JSON.stringify(option.label)}`).join(", ");
8312
+ return ` ${JSON.stringify(key)}: { ${entries} }`;
8313
+ }).join(",\n");
8314
+ const dynamicRowsBlock = ctx.hasCreatableSelectFields ? `
8315
+ const dynamicRows = await db
8316
+ .select({
8317
+ fieldName: ${ctx.selectOptionsTableVar}.fieldName,
8318
+ label: ${ctx.selectOptionsTableVar}.label,
8319
+ value: ${ctx.selectOptionsTableVar}.value
8320
+ })
8321
+ .from(${ctx.selectOptionsTableVar})
8322
+
8323
+ for (const row of dynamicRows) {
8324
+ labelMaps[row.fieldName] ??= {}
8325
+ labelMaps[row.fieldName][row.value] = row.label
8326
+ }
8327
+ ` : "";
8328
+ const assignments = ctx.selectFields.map((readField) => {
8329
+ const key = readField.path.join(".");
8330
+ return ` resolveSelectFieldPath(source, ${JSON.stringify(readField.path)}, labelMaps[${JSON.stringify(key)}] ?? {})`;
8331
+ }).join("\n");
8332
+ return `const STATIC_SELECT_OPTION_LABELS = {
8333
+ ${staticLabelMaps}
8334
+ } satisfies Record<string, Record<string, string>>
8335
+
8336
+ function cloneSelectOptionLabelMaps(): Record<string, Record<string, string>> {
8337
+ return Object.fromEntries(
8338
+ Object.entries(STATIC_SELECT_OPTION_LABELS).map(([fieldName, labels]) => [
8339
+ fieldName,
8340
+ { ...labels }
8341
+ ])
8342
+ )
8343
+ }
8344
+
8345
+ function toSelectOptionValues(value: unknown, labels: Record<string, string>): SelectOptionValue[] {
8346
+ const values = Array.isArray(value) ? value : value ? [value] : []
8347
+
8348
+ return values
8349
+ .filter((item): item is string => typeof item === 'string' && item.length > 0)
8350
+ .map((item) => ({
8351
+ label: labels[item] ?? item,
8352
+ value: item
8353
+ }))
8354
+ }
8355
+
8356
+ function resolveSelectFieldPath(
8357
+ source: Record<string, unknown>,
8358
+ path: readonly string[],
8359
+ labels: Record<string, string>
8360
+ ): void {
8361
+ const [fieldName, ...rest] = path
8362
+ if (!fieldName) return
8363
+
8364
+ if (rest.length === 0) {
8365
+ source[fieldName] = toSelectOptionValues(source[fieldName], labels)
8366
+ return
8367
+ }
8368
+
8369
+ const value = source[fieldName]
8370
+ if (Array.isArray(value)) {
8371
+ for (const item of value) {
8372
+ if (item && typeof item === 'object') {
8373
+ resolveSelectFieldPath(item as Record<string, unknown>, rest, labels)
8374
+ }
8375
+ }
8376
+ return
8377
+ }
8378
+
8379
+ if (value && typeof value === 'object') {
8380
+ resolveSelectFieldPath(value as Record<string, unknown>, rest, labels)
8381
+ }
8382
+ }
8383
+
8384
+ export async function resolve${ctx.Singular}SelectFields(rows: ${ctx.Singular}[]): Promise<${ctx.Singular}[]> {
8385
+ if (rows.length === 0) return rows
8386
+
8387
+ const labelMaps = cloneSelectOptionLabelMaps()
8388
+ ${dynamicRowsBlock}
8389
+ for (const row of rows) {
8390
+ const source = row as unknown as Record<string, unknown>
8391
+ ${assignments}
8392
+ }
8393
+
8394
+ return rows
8395
+ }`;
8396
+ }
8167
8397
  function genGetPluralContent(ctx) {
8168
8398
  const dbImports = [ctx.tableVar, "user", ...ctx.relTableImports].sort();
8169
8399
  const drizzle = ["asc", "desc"];
@@ -8176,7 +8406,8 @@ function genGetPluralContent(ctx) {
8176
8406
  if (ctx.hasRelationships) drizzle.push("eq");
8177
8407
  const sortedDrizzle = [...new Set(drizzle)].sort();
8178
8408
  const typeImports = [`${ctx.Plural}Page`, `${ctx.Plural}Query`, `${ctx.Singular}`];
8179
- const helperImports = [];
8409
+ const helperImports = ["formatActionDate"];
8410
+ if (ctx.hasSelectFields) helperImports.push(`resolve${ctx.Singular}SelectFields`);
8180
8411
  if (ctx.hasListRels) helperImports.push(`populate${ctx.Singular}ListRelationships`);
8181
8412
  if (ctx.hasMediaFields) helperImports.push(`resolve${ctx.Singular}MediaFields`);
8182
8413
  const populateImport = helperImports.length > 0 ? `
@@ -8266,7 +8497,8 @@ function genGetByIdContent(ctx) {
8266
8497
  const dbImports = [ctx.tableVar, "user", ...ctx.relTableImports].sort();
8267
8498
  const drizzle = ["eq"];
8268
8499
  const sortedDrizzle = [...new Set(drizzle)].sort();
8269
- const byIdHelperImports = [];
8500
+ const byIdHelperImports = ["formatActionDate"];
8501
+ if (ctx.hasSelectFields) byIdHelperImports.push(`resolve${ctx.Singular}SelectFields`);
8270
8502
  if (ctx.hasListRels) byIdHelperImports.push(`populate${ctx.Singular}ListRelationships`);
8271
8503
  if (ctx.hasMediaFields) byIdHelperImports.push(`resolve${ctx.Singular}MediaFields`);
8272
8504
  const populateImport = byIdHelperImports.length > 0 ? `
@@ -8304,7 +8536,8 @@ function genGetBySlugContent(ctx) {
8304
8536
  const dbImports = [ctx.tableVar, "user", ...ctx.relTableImports].sort();
8305
8537
  const drizzle = ["eq"];
8306
8538
  const sortedDrizzle = [...new Set(drizzle)].sort();
8307
- const bySlugHelperImports = [];
8539
+ const bySlugHelperImports = ["formatActionDate"];
8540
+ if (ctx.hasSelectFields) bySlugHelperImports.push(`resolve${ctx.Singular}SelectFields`);
8308
8541
  if (ctx.hasListRels) bySlugHelperImports.push(`populate${ctx.Singular}ListRelationships`);
8309
8542
  if (ctx.hasMediaFields) bySlugHelperImports.push(`resolve${ctx.Singular}MediaFields`);
8310
8543
  const populateImport = bySlugHelperImports.length > 0 ? `
@@ -8340,7 +8573,7 @@ export async function get${ctx.Singular}BySlug(slug: string): Promise<${ctx.Sing
8340
8573
  function genCreateContent(ctx) {
8341
8574
  const kebabSingular = toKebabCase(ctx.singular);
8342
8575
  const snapshotImport = ctx.hasM2M ? `build${ctx.Singular}VersionSnapshotWithRelationships` : `build${ctx.Singular}VersionSnapshot`;
8343
- const snapshotData = ctx.hasM2M ? `await build${ctx.Singular}VersionSnapshotWithRelationships(createdRow as ${ctx.Singular}, tx)` : `build${ctx.Singular}VersionSnapshot(createdRow as ${ctx.Singular})`;
8576
+ const snapshotData = ctx.hasM2M ? `await build${ctx.Singular}VersionSnapshotWithRelationships(createdRow as unknown as ${ctx.Singular}, tx)` : `build${ctx.Singular}VersionSnapshot(createdRow as unknown as ${ctx.Singular})`;
8344
8577
  const slugImport = ctx.hasAutoSlug ? `
8345
8578
  import { slugify } from '@admin/utils/validation/validation'` : "";
8346
8579
  const markdownImport = ctx.hasHtmlOutput ? `
@@ -8434,7 +8667,7 @@ ${ctx.htmlCreateMappings},` : ""}${ctx.hasDraft ? `
8434
8667
  typeof created${ctx.Singular}Id === 'string'
8435
8668
  ? await get${ctx.Singular}ById(created${ctx.Singular}Id)
8436
8669
  : null
8437
- const created${ctx.Singular}Data = created${ctx.Singular} ?? (result[0] as ${ctx.Singular})
8670
+ const created${ctx.Singular}Data = created${ctx.Singular} ?? (result[0] as unknown as ${ctx.Singular})
8438
8671
  const created${ctx.Singular}Context = ${createResultContext}
8439
8672
  ${afterHook("afterCreate", `created${ctx.Singular}Context`)}
8440
8673
  ${createAfterPublishBlock}
@@ -8453,7 +8686,7 @@ ${createAfterPublishBlock}
8453
8686
  function genUpdateContent(ctx) {
8454
8687
  const kebabSingular = toKebabCase(ctx.singular);
8455
8688
  const snapshotImport = ctx.hasM2M ? `build${ctx.Singular}VersionSnapshotWithRelationships` : `build${ctx.Singular}VersionSnapshot`;
8456
- const previousSnapshot = ctx.hasM2M ? `await build${ctx.Singular}VersionSnapshotWithRelationships(previousRow as ${ctx.Singular}, tx)` : `build${ctx.Singular}VersionSnapshot(previousRow as ${ctx.Singular})`;
8689
+ const previousSnapshot = ctx.hasM2M ? `await build${ctx.Singular}VersionSnapshotWithRelationships(previousRow as unknown as ${ctx.Singular}, tx)` : `build${ctx.Singular}VersionSnapshot(previousRow as unknown as ${ctx.Singular})`;
8457
8690
  const slugImport = ctx.hasAutoSlug ? `
8458
8691
  import { slugify } from '@admin/utils/validation/validation'` : "";
8459
8692
  const markdownImport = ctx.hasHtmlOutput ? `
@@ -8577,7 +8810,7 @@ ${htmlUpdateBlock}
8577
8810
  ${ctx.cacheInvalidationFn}(${ctx.camelPlural}CacheTags.byId(id))
8578
8811
  ${ctx.cacheInvalidationFn}(entityVersionsCacheTags.for('${ctx.plural}', id))
8579
8812
  const updated${ctx.Singular} = await get${ctx.Singular}ById(id)
8580
- const updated${ctx.Singular}Data = updated${ctx.Singular} ?? (result[0] as ${ctx.Singular})
8813
+ const updated${ctx.Singular}Data = updated${ctx.Singular} ?? (result[0] as unknown as ${ctx.Singular})
8581
8814
  const updated${ctx.Singular}Context = ${updateResultContext}
8582
8815
  ${updateAfterPublishBlock}
8583
8816
  ${afterHook("afterUpdate", `updated${ctx.Singular}Context`)}
@@ -8632,7 +8865,7 @@ function buildRestoreManyToManyStatements(ctx) {
8632
8865
  function genRestoreContent(ctx) {
8633
8866
  const kebabSingular = toKebabCase(ctx.singular);
8634
8867
  const snapshotImport = ctx.hasM2M ? `build${ctx.Singular}VersionSnapshotWithRelationships` : `build${ctx.Singular}VersionSnapshot`;
8635
- const currentSnapshot = ctx.hasM2M ? `await build${ctx.Singular}VersionSnapshotWithRelationships(currentRow as ${ctx.Singular}, tx)` : `build${ctx.Singular}VersionSnapshot(currentRow as ${ctx.Singular})`;
8868
+ const currentSnapshot = ctx.hasM2M ? `await build${ctx.Singular}VersionSnapshotWithRelationships(currentRow as unknown as ${ctx.Singular}, tx)` : `build${ctx.Singular}VersionSnapshot(currentRow as unknown as ${ctx.Singular})`;
8636
8869
  const dbImports = [
8637
8870
  "entityVersions",
8638
8871
  ctx.tableVar,
@@ -8711,7 +8944,7 @@ ${restoreM2M}
8711
8944
 
8712
8945
  return {
8713
8946
  success: true,
8714
- ${ctx.camelSingular}: restored${ctx.Singular} ?? (result[0] as ${ctx.Singular})
8947
+ ${ctx.camelSingular}: restored${ctx.Singular} ?? (result[0] as unknown as ${ctx.Singular})
8715
8948
  }
8716
8949
  } catch (error) {
8717
8950
  console.error('Error restoring ${ctx.singular} version:', error)
@@ -9754,6 +9987,8 @@ function buildEntityContext(schema, nextMajorVersion) {
9754
9987
  (f) => f.type === "select" && f.creatable === true
9755
9988
  );
9756
9989
  const hasCreatableSelectFields = creatableSelectFields.length > 0;
9990
+ const selectFields = collectReadSelectFields(allDbFields);
9991
+ const hasSelectFields = selectFields.length > 0;
9757
9992
  const dateRangeFilterFields = new Set(
9758
9993
  resolvedFilters.filter((filter) => filter.type === "date-range").map((filter) => filter.field)
9759
9994
  );
@@ -9777,7 +10012,7 @@ function buildEntityContext(schema, nextMajorVersion) {
9777
10012
  const relTableImports = relationshipFields.map((f) => toCamelCase(f.relationship));
9778
10013
  const listRelTableImports = allListRelQueries.map((q) => q.relTable);
9779
10014
  const dataFields = allDbFields.map(
9780
- (f) => ` ${quotePropertyName(f.name)}: ${getFieldType(f, "output")}${f.required || f.primaryKey ? "" : " | null"}`
10015
+ (f) => ` ${quotePropertyName(f.name)}: ${getReadFieldType(f)}${f.type === "select" || f.required || f.primaryKey ? "" : " | null"}`
9781
10016
  ).join("\n");
9782
10017
  const m2mFieldTypes = m2mFields.map((f) => ` ${quotePropertyName(f.name)}: string[]`).join("\n");
9783
10018
  const htmlFieldTypes = htmlOutputFields.map((f) => ` ${f.name}Html: string`).join("\n");
@@ -9788,13 +10023,19 @@ function buildEntityContext(schema, nextMajorVersion) {
9788
10023
  const mediaTypeImport = hasMediaFields ? `
9789
10024
  import type { AdminMedia } from '@admin/types'
9790
10025
  ` : "";
9791
- const dataInterface = `${mediaTypeImport}${buildAuthorshipUserType()}
10026
+ const hasTimestampFields = allDbFields.some((f) => f.name === "createdAt") && allDbFields.some((f) => f.name === "updatedAt");
10027
+ const hasFormattedTimestampFields = allDbFields.some((f) => f.name === "createdAtFormatted");
10028
+ const timestampFields = hasTimestampFields && !hasFormattedTimestampFields ? "\n createdAtFormatted: string\n updatedAtFormatted: string" : "";
10029
+ const readSelectOptionType = buildReadSelectOptionType(selectFields);
10030
+ const dataInterface = `${mediaTypeImport}${buildAuthorshipUserType()}${readSelectOptionType ? `
10031
+
10032
+ ${readSelectOptionType}` : ""}
9792
10033
 
9793
10034
  export interface ${Singular} {
9794
10035
  ${dataFields}${htmlFieldTypes ? `
9795
10036
  ${htmlFieldTypes}` : ""}${m2mFieldTypes ? `
9796
10037
  ${m2mFieldTypes}` : ""}${mediaCompanionTypes ? `
9797
- ${mediaCompanionTypes}` : ""}
10038
+ ${mediaCompanionTypes}` : ""}${timestampFields}
9798
10039
  ${buildAuthorshipDataFields()}
9799
10040
  }`;
9800
10041
  const responseInterface = `export interface ${Plural}Page {
@@ -9893,7 +10134,8 @@ ${searchFields.map((f) => ` ilike(${tableVar}.${f}, searchTerm)`).join(
9893
10134
  Singular,
9894
10135
  hasListRels
9895
10136
  );
9896
- const resultMapping = hasMediaFields ? wrapWithMediaResolution(baseResultMapping, camelPlural, Singular) : baseResultMapping;
10137
+ const selectResultMapping = hasSelectFields ? wrapWithSelectResolution(baseResultMapping, camelPlural, Singular) : baseResultMapping;
10138
+ const resultMapping = hasMediaFields ? wrapWithMediaResolution(selectResultMapping, camelPlural, Singular) : selectResultMapping;
9897
10139
  const baseListResultMapping = hasHtmlOutput ? buildResultMapping(
9898
10140
  listDbFields,
9899
10141
  relationshipFields,
@@ -9902,7 +10144,8 @@ ${searchFields.map((f) => ` ilike(${tableVar}.${f}, searchTerm)`).join(
9902
10144
  Singular,
9903
10145
  hasListRels
9904
10146
  ) : baseResultMapping;
9905
- const listResultMapping = hasMediaFields ? wrapWithMediaResolution(baseListResultMapping, camelPlural, Singular) : baseListResultMapping;
10147
+ const selectListResultMapping = hasSelectFields ? wrapWithSelectResolution(baseListResultMapping, camelPlural, Singular) : baseListResultMapping;
10148
+ const listResultMapping = hasMediaFields ? wrapWithMediaResolution(selectListResultMapping, camelPlural, Singular) : selectListResultMapping;
9906
10149
  const fieldMeta = createFields.map((f) => `{ name: '${f.name}', type: '${f.type}', required: ${f.required ?? false} }`).join(",\n ");
9907
10150
  const createMappings = createFields.map((f) => ` ${f.name}: ${generateFieldMapping(f)}`).join(",\n");
9908
10151
  const htmlCreateBlock = hasHtmlOutput ? `
@@ -9943,9 +10186,11 @@ ${searchFields.map((f) => ` ilike(${tableVar}.${f}, searchTerm)`).join(
9943
10186
  return `const row = result[0]
9944
10187
  return ${singleRowMapping}`;
9945
10188
  })();
9946
- const singleRowReturn = hasMediaFields ? `${baseSingleRowReturn.replace(/return ([\s\S]+)$/, `const singleRow = $1
9947
- const [resolved] = await resolve${Singular}MediaFields([singleRow])
9948
- return resolved`)}` : baseSingleRowReturn;
10189
+ const singleRowResolvers = [
10190
+ ...hasSelectFields ? [`resolve${Singular}SelectFields`] : [],
10191
+ ...hasMediaFields ? [`resolve${Singular}MediaFields`] : []
10192
+ ];
10193
+ const singleRowReturn = singleRowResolvers.length > 0 ? wrapSingleRowReturnWithResolvers(baseSingleRowReturn, singleRowResolvers) : baseSingleRowReturn;
9949
10194
  const nextMajor = nextMajorVersion ?? 16;
9950
10195
  const cacheInvalidationFn = nextMajor >= 16 ? "updateTag" : "revalidateTag";
9951
10196
  const cacheReadImport = nextMajor >= 16 ? `import { cacheLife, cacheTag } from 'next/cache'` : `import { unstable_cacheTag as cacheTag } from 'next/cache'`;
@@ -9975,9 +10220,11 @@ ${searchFields.map((f) => ` ilike(${tableVar}.${f}, searchTerm)`).join(
9975
10220
  hasSearch,
9976
10221
  hasFilters,
9977
10222
  hasAutoSlug,
10223
+ hasSelectFields,
9978
10224
  hasCreatableSelectFields,
9979
10225
  searchFields,
9980
10226
  htmlOutputFields,
10227
+ selectFields,
9981
10228
  creatableSelectFields,
9982
10229
  m2mFields,
9983
10230
  filterableFields,
@@ -10028,11 +10275,135 @@ function wrapWithMediaResolution(mapping, camelPlural, Singular) {
10028
10275
  }
10029
10276
  return mapping.replace(
10030
10277
  new RegExp(`${camelPlural}: (results as ${Singular}\\[\\])`),
10031
- `${camelPlural}: await resolve${Singular}MediaFields($1)`
10278
+ `${camelPlural}: await resolve${Singular}MediaFields($1 as unknown as ${Singular}[])`
10279
+ );
10280
+ }
10281
+ function wrapWithSelectResolution(mapping, camelPlural, Singular) {
10282
+ const returnPattern = new RegExp(
10283
+ `return \\{\\n\\s+${camelPlural}: ([^,]+),\\n\\s+total\\n\\s+\\}`
10284
+ );
10285
+ const match = mapping.match(returnPattern);
10286
+ if (match) {
10287
+ const arrayExpr = match[1].trim();
10288
+ return mapping.replace(
10289
+ match[0],
10290
+ `const resolved${Singular}SelectFields = await resolve${Singular}SelectFields(${arrayExpr})
10291
+
10292
+ return {
10293
+ ${camelPlural}: resolved${Singular}SelectFields,
10294
+ total
10295
+ }`
10296
+ );
10297
+ }
10298
+ return mapping.replace(
10299
+ new RegExp(`${camelPlural}: (results as ${Singular}\\[\\])`),
10300
+ `${camelPlural}: await resolve${Singular}SelectFields($1 as unknown as ${Singular}[])`
10032
10301
  );
10033
10302
  }
10303
+ function wrapSingleRowReturnWithResolvers(singleRowReturn, resolvers) {
10304
+ return singleRowReturn.replace(/return ([\s\S]+)$/, (_, expression) => {
10305
+ const lines = [`const singleRow = ${expression}`];
10306
+ let current = "singleRow";
10307
+ for (const resolver of resolvers) {
10308
+ const resolvedName = resolver.replace(/^resolve/, "resolved").replace(/Fields$/, "Row");
10309
+ lines.push(` const [${resolvedName}] = await ${resolver}([${current}])`);
10310
+ current = resolvedName;
10311
+ }
10312
+ lines.push(` return ${current}`);
10313
+ return lines.join("\n");
10314
+ });
10315
+ }
10034
10316
 
10035
10317
  // adapters/next/generators/actions/single-actions.ts
10318
+ function generateSingleResolveSelectFieldsFunction(Singular, selectFields, selectOptionsTableVar, hasCreatableSelectFields) {
10319
+ const staticLabelMaps = selectFields.map((readField) => {
10320
+ const key = readField.path.join(".");
10321
+ const entries = (readField.field.options ?? []).map((option) => `${JSON.stringify(option.value)}: ${JSON.stringify(option.label)}`).join(", ");
10322
+ return ` ${JSON.stringify(key)}: { ${entries} }`;
10323
+ }).join(",\n");
10324
+ const dynamicRowsBlock = hasCreatableSelectFields ? `
10325
+ const dynamicRows = await db
10326
+ .select({
10327
+ fieldName: ${selectOptionsTableVar}.fieldName,
10328
+ label: ${selectOptionsTableVar}.label,
10329
+ value: ${selectOptionsTableVar}.value
10330
+ })
10331
+ .from(${selectOptionsTableVar})
10332
+
10333
+ for (const row of dynamicRows) {
10334
+ labelMaps[row.fieldName] ??= {}
10335
+ labelMaps[row.fieldName][row.value] = row.label
10336
+ }
10337
+ ` : "";
10338
+ const assignments = selectFields.map((readField) => {
10339
+ const key = readField.path.join(".");
10340
+ return ` resolveSelectFieldPath(source, ${JSON.stringify(readField.path)}, labelMaps[${JSON.stringify(key)}] ?? {})`;
10341
+ }).join("\n");
10342
+ return `const STATIC_SELECT_OPTION_LABELS = {
10343
+ ${staticLabelMaps}
10344
+ } satisfies Record<string, Record<string, string>>
10345
+
10346
+ function cloneSelectOptionLabelMaps(): Record<string, Record<string, string>> {
10347
+ return Object.fromEntries(
10348
+ Object.entries(STATIC_SELECT_OPTION_LABELS).map(([fieldName, labels]) => [
10349
+ fieldName,
10350
+ { ...labels }
10351
+ ])
10352
+ )
10353
+ }
10354
+
10355
+ function toSelectOptionValues(value: unknown, labels: Record<string, string>): SelectOptionValue[] {
10356
+ const values = Array.isArray(value) ? value : value ? [value] : []
10357
+
10358
+ return values
10359
+ .filter((item): item is string => typeof item === 'string' && item.length > 0)
10360
+ .map((item) => ({
10361
+ label: labels[item] ?? item,
10362
+ value: item
10363
+ }))
10364
+ }
10365
+
10366
+ function resolveSelectFieldPath(
10367
+ source: Record<string, unknown>,
10368
+ path: readonly string[],
10369
+ labels: Record<string, string>
10370
+ ): void {
10371
+ const [fieldName, ...rest] = path
10372
+ if (!fieldName) return
10373
+
10374
+ if (rest.length === 0) {
10375
+ source[fieldName] = toSelectOptionValues(source[fieldName], labels)
10376
+ return
10377
+ }
10378
+
10379
+ const value = source[fieldName]
10380
+ if (Array.isArray(value)) {
10381
+ for (const item of value) {
10382
+ if (item && typeof item === 'object') {
10383
+ resolveSelectFieldPath(item as Record<string, unknown>, rest, labels)
10384
+ }
10385
+ }
10386
+ return
10387
+ }
10388
+
10389
+ if (value && typeof value === 'object') {
10390
+ resolveSelectFieldPath(value as Record<string, unknown>, rest, labels)
10391
+ }
10392
+ }
10393
+
10394
+ async function resolve${Singular}SelectFields(rows: ${Singular}[]): Promise<${Singular}[]> {
10395
+ if (rows.length === 0) return rows
10396
+
10397
+ const labelMaps = cloneSelectOptionLabelMaps()
10398
+ ${dynamicRowsBlock}
10399
+ for (const row of rows) {
10400
+ const source = row as unknown as Record<string, unknown>
10401
+ ${assignments}
10402
+ }
10403
+
10404
+ return rows
10405
+ }`;
10406
+ }
10036
10407
  function generateSingleActions(schema, actionsDir, options = {}) {
10037
10408
  const singular = singularize(schema.name);
10038
10409
  const plural = pluralize(schema.name);
@@ -10052,6 +10423,8 @@ function generateSingleActions(schema, actionsDir, options = {}) {
10052
10423
  (field) => field.type === "select" && field.creatable === true
10053
10424
  );
10054
10425
  const hasCreatableSelectFields = creatableSelectFields.length > 0;
10426
+ const selectFields = collectReadSelectFields(dbFields);
10427
+ const hasSelectFields = selectFields.length > 0;
10055
10428
  const allDbFields = [...dbFields];
10056
10429
  if (!dbFields.some((f) => f.name === "createdAt")) {
10057
10430
  allDbFields.push({ name: "createdAt", type: "timestamp", required: true });
@@ -10068,9 +10441,12 @@ function generateSingleActions(schema, actionsDir, options = {}) {
10068
10441
  (f) => !f.primaryKey && f.name !== "createdAt" && f.name !== "updatedAt" && !isAuthorshipFieldName(f.name)
10069
10442
  );
10070
10443
  const dataFields = allDbFields.map(
10071
- (f) => ` ${quotePropertyName(f.name)}: ${getFieldType(f, "output")}${f.required || f.primaryKey ? "" : " | null"}`
10444
+ (f) => ` ${quotePropertyName(f.name)}: ${getReadFieldType(f)}${f.type === "select" || f.required || f.primaryKey ? "" : " | null"}`
10072
10445
  ).join("\n");
10073
10446
  const htmlFieldTypes = htmlOutputFields.map((f) => ` ${f.name}Html: string`).join("\n");
10447
+ const hasTimestampFields = allDbFields.some((f) => f.name === "createdAt") && allDbFields.some((f) => f.name === "updatedAt");
10448
+ const hasFormattedTimestampFields = allDbFields.some((f) => f.name === "createdAtFormatted");
10449
+ const timestampFields = hasTimestampFields && !hasFormattedTimestampFields ? "\n createdAtFormatted: string\n updatedAtFormatted: string" : "";
10074
10450
  const upsertInterfaceFields = upsertFields.map((f) => ` ${quotePropertyName(f.name)}?: ${getFieldType(f, "input")}`).join("\n");
10075
10451
  const fieldMeta = upsertFields.map((f) => `{ name: '${f.name}', type: '${f.type}', required: ${f.required ?? false} }`).join(",\n ");
10076
10452
  const cacheTag = `${schema.name}:all`;
@@ -10094,9 +10470,10 @@ function generateSingleActions(schema, actionsDir, options = {}) {
10094
10470
  };
10095
10471
  const typesContent = `${[
10096
10472
  buildAuthorshipUserType(),
10473
+ buildReadSelectOptionType(selectFields),
10097
10474
  `export interface ${Singular} {
10098
10475
  ${dataFields}${htmlFieldTypes ? `
10099
- ${htmlFieldTypes}` : ""}
10476
+ ${htmlFieldTypes}` : ""}${timestampFields}
10100
10477
  ${buildAuthorshipDataFields()}
10101
10478
  }`,
10102
10479
  `export interface ${Singular}UpsertInput {
@@ -10122,22 +10499,47 @@ ${upsertInterfaceFields}
10122
10499
  authorshipSelects,
10123
10500
  authorshipJoins
10124
10501
  );
10125
- const singleRowReturn = `const row = result[0]
10126
- return ${buildSingleRowMapping(allDbFields, [], `${Singular}`, htmlOutputFields)}`;
10502
+ const singleRowMapping = buildSingleRowMapping(allDbFields, [], `${Singular}`, htmlOutputFields);
10503
+ const singleRowReturn = hasSelectFields ? `const row = result[0]
10504
+ const singleRow = ${singleRowMapping}
10505
+ const [resolvedSelectRow] = await resolve${Singular}SelectFields([singleRow])
10506
+ return resolvedSelectRow` : `const row = result[0]
10507
+ return ${singleRowMapping}`;
10508
+ const getDbImports = [
10509
+ tableVar,
10510
+ "user",
10511
+ ...hasCreatableSelectFields ? [`${tableVar}SelectOptions`] : []
10512
+ ].sort();
10513
+ const selectResolutionFunctions = hasSelectFields ? `
10514
+ ${generateSingleResolveSelectFieldsFunction(Singular, selectFields, `${tableVar}SelectOptions`, hasCreatableSelectFields)}
10515
+ ` : "";
10127
10516
  const getContent = `'use server'
10128
10517
 
10129
10518
  import db from '@admin/db'
10130
- import { ${tableVar}, user } from '@admin/db/schema'
10519
+ import { ${getDbImports.join(", ")} } from '@admin/db/schema'
10131
10520
  import { eq } from 'drizzle-orm'
10132
10521
  import { alias } from 'drizzle-orm/pg-core'
10133
10522
  ${cacheReadImport}
10134
10523
  import { ${cacheTagsVar} } from './types'
10135
- import type { ${Singular} } from './types'
10524
+ import type { ${Singular}${hasSelectFields ? ", SelectOptionValue" : ""} } from './types'
10136
10525
 
10137
10526
  ${buildAuthorshipAliasBlock()}
10138
10527
 
10139
10528
  const SINGLETON_ID = '${singletonId}'
10140
10529
 
10530
+ function formatActionDate(value: unknown): string {
10531
+ if (typeof value !== 'string') return ''
10532
+ const date = new Date(value)
10533
+ if (Number.isNaN(date.getTime())) return ''
10534
+
10535
+ return date.toLocaleDateString('en-US', {
10536
+ month: 'short',
10537
+ day: '2-digit',
10538
+ year: 'numeric',
10539
+ timeZone: 'UTC'
10540
+ })
10541
+ }
10542
+ ${selectResolutionFunctions}
10141
10543
  export async function get${Singular}(): Promise<${Singular} | null> {
10142
10544
  'use cache'${cacheLifeLine}
10143
10545
  cacheTag(${cacheTagsVar}.all)
@@ -10217,7 +10619,7 @@ ${htmlUpsertBlock}
10217
10619
 
10218
10620
  return {
10219
10621
  success: true,
10220
- ${camelSingular}: saved${Singular} ?? (result[0] as ${Singular})
10622
+ ${camelSingular}: saved${Singular} ?? (result[0] as unknown as ${Singular})
10221
10623
  }
10222
10624
  } catch (error) {
10223
10625
  console.error('Error upserting ${singular}:', error)
@@ -10446,6 +10848,22 @@ function fieldHasMediaCompanion(column, fields) {
10446
10848
  const field = fields.find((item) => item.name === column.accessorKey);
10447
10849
  return field?.type === "image" || field?.type === "media" || field?.type === "video";
10448
10850
  }
10851
+ function fieldIsSelect(column, fields) {
10852
+ return fields.find((item) => item.name === column.accessorKey)?.type === "select";
10853
+ }
10854
+ function generateSelectLabel(valueExpression) {
10855
+ return `const label = Array.isArray(${valueExpression})
10856
+ ? ${valueExpression}
10857
+ .map((option) =>
10858
+ typeof option === 'object' && option !== null && 'label' in option
10859
+ ? String(option.label)
10860
+ : String(option)
10861
+ )
10862
+ .join(', ')
10863
+ : ${valueExpression} === null || ${valueExpression} === undefined
10864
+ ? null
10865
+ : String(${valueExpression})`;
10866
+ }
10449
10867
  function buildImageUrlExpression(column, fields, fallbackExpression) {
10450
10868
  const fallback = `(${fallbackExpression} as string | null | undefined)`;
10451
10869
  if (!fieldHasMediaCompanion(column, fields)) {
@@ -10526,6 +10944,7 @@ function generateColumnDef2(column, fields) {
10526
10944
  }` : `header: '${column.header}'`;
10527
10945
  let cellDef = "";
10528
10946
  const authorshipUserAccessor = getAuthorshipUserAccessor(column.accessorKey);
10947
+ const isSelectColumn = fieldIsSelect(column, fields);
10529
10948
  if (authorshipUserAccessor) {
10530
10949
  cellDef = `cell: ({ row }) => {
10531
10950
  ${generateAuthorshipValue(authorshipUserAccessor)}
@@ -10546,7 +10965,15 @@ function generateColumnDef2(column, fields) {
10546
10965
  }`;
10547
10966
  break;
10548
10967
  case "badge":
10549
- cellDef = `cell: ({ row }) => {
10968
+ cellDef = isSelectColumn ? `cell: ({ row }) => {
10969
+ const value = row.getValue('${column.accessorKey}')
10970
+ ${generateSelectLabel("value")}
10971
+ return (
10972
+ <Badge variant="outline">
10973
+ {label || 'Unavailable'}
10974
+ </Badge>
10975
+ )
10976
+ }` : `cell: ({ row }) => {
10550
10977
  const value = row.getValue('${column.accessorKey}')
10551
10978
  return (
10552
10979
  <Badge variant="outline">
@@ -10628,14 +11055,22 @@ function generateColumnDef2(column, fields) {
10628
11055
  return <${column.component} data={row.original} />
10629
11056
  }`;
10630
11057
  } else {
10631
- cellDef = `cell: ({ row }) => {
11058
+ cellDef = isSelectColumn ? `cell: ({ row }) => {
11059
+ const value = row.getValue('${column.accessorKey}')
11060
+ ${generateSelectLabel("value")}
11061
+ return <div>{label || 'Unavailable'}</div>
11062
+ }` : `cell: ({ row }) => {
10632
11063
  const value = row.getValue('${column.accessorKey}')
10633
11064
  return <div>{value === null || value === undefined ? 'Unavailable' : String(value)}</div>
10634
11065
  }`;
10635
11066
  }
10636
11067
  break;
10637
11068
  default:
10638
- cellDef = `cell: ({ row }) => {
11069
+ cellDef = isSelectColumn ? `cell: ({ row }) => {
11070
+ const value = row.getValue('${column.accessorKey}')
11071
+ ${generateSelectLabel("value")}
11072
+ return <div>{label || 'Unavailable'}</div>
11073
+ }` : `cell: ({ row }) => {
10639
11074
  const value = row.getValue('${column.accessorKey}')
10640
11075
  return <div>{value === null || value === undefined ? 'Unavailable' : String(value)}</div>
10641
11076
  }`;
@@ -10677,6 +11112,7 @@ function generateFirstColumnDef(column, fields, options = {}) {
10677
11112
  )`;
10678
11113
  let cellDef = "";
10679
11114
  const authorshipUserAccessor = getAuthorshipUserAccessor(column.accessorKey);
11115
+ const isSelectColumn = fieldIsSelect(column, fields);
10680
11116
  if (authorshipUserAccessor) {
10681
11117
  cellDef = `cell: ({ row }) => {
10682
11118
  ${generateAuthorshipValue(authorshipUserAccessor)}
@@ -10708,7 +11144,22 @@ function generateFirstColumnDef(column, fields, options = {}) {
10708
11144
  }`;
10709
11145
  break;
10710
11146
  case "badge":
10711
- cellDef = `cell: ({ row }) => {
11147
+ cellDef = isSelectColumn ? `cell: ({ row }) => {
11148
+ const value = row.original.${column.accessorKey}
11149
+ ${generateSelectLabel("value")}
11150
+ return (
11151
+ <div className="flex items-center gap-4 h-full pl-1">
11152
+ <Checkbox
11153
+ checked={row.getIsSelected()}
11154
+ onCheckedChange={(value) => row.toggleSelected(!!value)}
11155
+ aria-label="Select row"
11156
+ />
11157
+ <Badge variant="outline">
11158
+ {label || 'Unavailable'}
11159
+ </Badge>
11160
+ </div>
11161
+ )
11162
+ }` : `cell: ({ row }) => {
10712
11163
  const value = row.original.${column.accessorKey}
10713
11164
  return (
10714
11165
  <div className="flex items-center gap-4 h-full pl-1">
@@ -10831,7 +11282,22 @@ function generateFirstColumnDef(column, fields, options = {}) {
10831
11282
  )
10832
11283
  }`;
10833
11284
  } else {
10834
- cellDef = `cell: ({ row }) => {
11285
+ cellDef = isSelectColumn ? `cell: ({ row }) => {
11286
+ const value = row.original.${column.accessorKey}
11287
+ ${generateSelectLabel("value")}
11288
+ return (
11289
+ <div className="flex items-center gap-4 h-full pl-1">
11290
+ <Checkbox
11291
+ checked={row.getIsSelected()}
11292
+ onCheckedChange={(value) => row.toggleSelected(!!value)}
11293
+ aria-label="Select row"
11294
+ />
11295
+ <span className="truncate max-w-[300px]">
11296
+ {label || 'Unavailable'}
11297
+ </span>
11298
+ </div>
11299
+ )
11300
+ }` : `cell: ({ row }) => {
10835
11301
  const value = row.original.${column.accessorKey}
10836
11302
  return (
10837
11303
  <div className="flex items-center gap-4 h-full pl-1">
@@ -10877,7 +11343,22 @@ function generateFirstColumnDef(column, fields, options = {}) {
10877
11343
  }`;
10878
11344
  break;
10879
11345
  default:
10880
- cellDef = `cell: ({ row }) => {
11346
+ cellDef = isSelectColumn ? `cell: ({ row }) => {
11347
+ const value = row.original.${column.accessorKey}
11348
+ ${generateSelectLabel("value")}
11349
+ return (
11350
+ <div className="flex items-center gap-4 h-full pl-1">
11351
+ <Checkbox
11352
+ checked={row.getIsSelected()}
11353
+ onCheckedChange={(value) => row.toggleSelected(!!value)}
11354
+ aria-label="Select row"
11355
+ />
11356
+ <span className="truncate max-w-[300px]">
11357
+ {label || 'Unavailable'}
11358
+ </span>
11359
+ </div>
11360
+ )
11361
+ }` : `cell: ({ row }) => {
10881
11362
  const value = row.original.${column.accessorKey}
10882
11363
  return (
10883
11364
  <div className="flex items-center gap-4 h-full pl-1">
@@ -11549,9 +12030,38 @@ function defaultFallbackValue(f) {
11549
12030
  function hasInitialValueExpression(fieldName, dataSource = "initialData") {
11550
12031
  return `${dataSource}?.${fieldName} !== undefined && ${dataSource}?.${fieldName} !== null && ${dataSource}?.${fieldName} !== ''`;
11551
12032
  }
12033
+ function selectReadValuesExpression(fieldName, dataSource = "initialData") {
12034
+ return `(() => {
12035
+ const selectValue = ${dataSource}?.${fieldName} as unknown
12036
+ const values = Array.isArray(selectValue) ? selectValue : [selectValue]
12037
+ return values
12038
+ .map((option) => {
12039
+ if (typeof option === 'string') return option
12040
+ if (option && typeof option === 'object' && 'value' in option) {
12041
+ return String(option.value)
12042
+ }
12043
+ return null
12044
+ })
12045
+ .filter((value): value is string => typeof value === 'string' && value.length > 0)
12046
+ })()`;
12047
+ }
11552
12048
  function generateDefaultValue(f, fieldMap, dataSource = "initialData") {
11553
12049
  if (f.defaultValueFrom && f.defaultValueFrom.trim() !== f.name && fieldMap?.has(f.defaultValueFrom.trim())) {
11554
12050
  const sourceName = f.defaultValueFrom.trim();
12051
+ if (f.type === "select") {
12052
+ const targetValuesExpression = selectReadValuesExpression(f.name, dataSource);
12053
+ const sourceValuesExpression = selectReadValuesExpression(sourceName, dataSource);
12054
+ if (f.multiple) {
12055
+ const fallback2 = JSON.stringify(selectDefaultValues(f));
12056
+ return ` ${f.name}: ${targetValuesExpression}.length > 0
12057
+ ? ${targetValuesExpression}
12058
+ : ${sourceValuesExpression}.length > 0
12059
+ ? ${sourceValuesExpression}
12060
+ : ${fallback2}`;
12061
+ }
12062
+ const fallback = f.default !== void 0 && f.default !== null ? JSON.stringify(f.default) : "''";
12063
+ return ` ${f.name}: ${targetValuesExpression}[0] ?? ${sourceValuesExpression}[0] ?? ${fallback}`;
12064
+ }
11555
12065
  return ` ${f.name}: ${hasInitialValueExpression(f.name, dataSource)}
11556
12066
  ? ${dataSource}.${f.name}
11557
12067
  : ${hasInitialValueExpression(sourceName, dataSource)}
@@ -11581,22 +12091,23 @@ function generateDefaultValue(f, fieldMap, dataSource = "initialData") {
11581
12091
  return ` ${f.name}: ${dataSource}?.${f.name} ?? undefined`;
11582
12092
  }
11583
12093
  if (f.type === "select") {
12094
+ const selectedValuesExpression = selectReadValuesExpression(f.name, dataSource);
11584
12095
  if (f.multiple) {
11585
12096
  const fallback2 = JSON.stringify(selectDefaultValues(f));
11586
12097
  if (f.creatable) {
11587
- return ` ${f.name}: Array.isArray(${dataSource}?.${f.name}) ? ${dataSource}?.${f.name} : ${fallback2}`;
12098
+ return ` ${f.name}: ${selectedValuesExpression}.length > 0 ? ${selectedValuesExpression} : ${fallback2}`;
11588
12099
  }
11589
12100
  const validValuesExpression2 = `([${(f.options || []).map((option) => JSON.stringify(option.value)).join(", ")}] as const as readonly string[])`;
11590
- return ` ${f.name}: Array.isArray(${dataSource}?.${f.name})
11591
- ? ${dataSource}?.${f.name}.filter((value) => ${validValuesExpression2}.includes(value))
12101
+ return ` ${f.name}: ${selectedValuesExpression}.length > 0
12102
+ ? ${selectedValuesExpression}.filter((value) => ${validValuesExpression2}.includes(value))
11592
12103
  : ${fallback2}`;
11593
12104
  }
11594
12105
  const fallback = f.default !== void 0 && f.default !== null ? JSON.stringify(f.default) : "''";
11595
12106
  if (f.creatable) {
11596
- return ` ${f.name}: typeof ${dataSource}?.${f.name} === 'string' ? ${dataSource}?.${f.name} : ${fallback}`;
12107
+ return ` ${f.name}: ${selectedValuesExpression}[0] ?? ${fallback}`;
11597
12108
  }
11598
12109
  const validValuesExpression = `([${(f.options || []).map((option) => JSON.stringify(option.value)).join(", ")}] as const as readonly string[])`;
11599
- return ` ${f.name}: typeof ${dataSource}?.${f.name} === 'string' && ${validValuesExpression}.includes(${dataSource}?.${f.name}) ? ${dataSource}?.${f.name} : ${fallback}`;
12110
+ return ` ${f.name}: ${selectedValuesExpression}[0] && ${validValuesExpression}.includes(${selectedValuesExpression}[0]) ? ${selectedValuesExpression}[0] : ${fallback}`;
11600
12111
  }
11601
12112
  if (f.default !== void 0 && f.default !== null) {
11602
12113
  const def = typeof f.default === "string" ? `'${f.default}'` : f.default;