@ghentcdh/create-crouton 0.0.1-alpha.86 → 0.0.1-alpha.87

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -24468,14 +24468,14 @@ function date4(params) {
24468
24468
  return _coercedDate(ZodDate, params);
24469
24469
  }
24470
24470
 
24471
- // ../crouton-core/src/lib/zod.types.ts
24471
+ // ../crouton-core/src/lib/request/zod.types.ts
24472
24472
  var PositiveRequestNumber = () => external_exports.coerce.number().int().positive().nonnegative();
24473
24473
  var StringOrArray = () => external_exports.string().or(external_exports.array(external_exports.string())).transform((val) => {
24474
24474
  if (Array.isArray(val)) return val;
24475
24475
  return [val];
24476
24476
  });
24477
24477
 
24478
- // ../crouton-core/src/lib/request.model.ts
24478
+ // ../crouton-core/src/lib/request/request.model.ts
24479
24479
  var SortDirEnum = external_exports.enum(["asc", "desc"]);
24480
24480
  var RequestSchema = external_exports.object({
24481
24481
  page: PositiveRequestNumber().optional().default(1),
@@ -24495,7 +24495,7 @@ var RequestSchemaWithOffset = RequestSchema.transform((schema) => {
24495
24495
  };
24496
24496
  });
24497
24497
 
24498
- // ../crouton-core/src/lib/filter.ts
24498
+ // ../crouton-core/src/lib/request/filter.ts
24499
24499
  var Operator = [
24500
24500
  "contains",
24501
24501
  "not_contains",
@@ -24521,7 +24521,7 @@ var OperatorOptions = Operator.map((k) => ({
24521
24521
  label: OperatorLabel[k]
24522
24522
  }));
24523
24523
 
24524
- // ../crouton-core/src/lib/response.model.ts
24524
+ // ../crouton-core/src/lib/request/response.model.ts
24525
24525
  var ResponseRequestSchema = RequestSchema.extend({
24526
24526
  count: PositiveRequestNumber(),
24527
24527
  totalPages: PositiveRequestNumber()
@@ -24568,44 +24568,6 @@ var ResourceTableActionSchema = external_exports.union([
24568
24568
  ResourceLinkActionSchema
24569
24569
  ]);
24570
24570
 
24571
- // ../crouton-core/src/lib/data-source/Security.schema.ts
24572
- var SecuritySchema = external_exports.union([
24573
- external_exports.object({ public: external_exports.literal(true).describe("When true, the route is public and requires no authentication.") }),
24574
- external_exports.object({ guard: external_exports.union([external_exports.string(), external_exports.array(external_exports.string()).min(1)]).describe("Named guard(s) that must all pass (AND logic). A single string or an array of guard names.") })
24575
- ]);
24576
-
24577
- // ../crouton-core/src/lib/data-source/Operations.schema.ts
24578
- var ExternalOpEntry = external_exports.object({
24579
- uri: external_exports.string().describe("Full URL of the external API endpoint that handles this operation."),
24580
- method: external_exports.string().optional().describe("HTTP method for the external endpoint. Defaults to the standard method for each operation."),
24581
- security: SecuritySchema.optional().describe("Security override for this specific external operation.")
24582
- });
24583
- var OpEntry = external_exports.union([
24584
- external_exports.boolean(),
24585
- ExternalOpEntry,
24586
- external_exports.object({ security: SecuritySchema.optional().describe("Security override for this specific operation. Overrides the resource-level global security block.") })
24587
- ]);
24588
- var FindAllOpEntry = external_exports.union([
24589
- external_exports.boolean(),
24590
- ExternalOpEntry.extend({
24591
- search: external_exports.array(external_exports.string()).optional().describe("Column ids (or dot paths) that ?q= matches."),
24592
- filter: external_exports.array(external_exports.string()).optional().describe("Optional allowlist of filter fields the server accepts.")
24593
- }),
24594
- external_exports.object({
24595
- security: SecuritySchema.optional().describe("Security override for this specific operation. Overrides the resource-level global security block."),
24596
- search: external_exports.array(external_exports.string()).optional().describe("Column ids (or dot paths) that ?q= matches."),
24597
- filter: external_exports.array(external_exports.string()).optional().describe("Optional allowlist of filter fields the server accepts.")
24598
- })
24599
- ]);
24600
- var JsonOperationsSchema = external_exports.object({
24601
- findAll: FindAllOpEntry.default(true).describe("GET collection endpoint. true = enabled, false = disabled, object = external URI or security override."),
24602
- findOne: OpEntry.default(true).describe("GET single record endpoint. true = enabled, false = disabled, object = external URI or security override."),
24603
- create: OpEntry.default(true).describe("POST create endpoint. true = enabled, false = disabled, object = external URI or security override."),
24604
- update: OpEntry.default(true).describe("PUT full-update endpoint. true = enabled, false = disabled, object = external URI or security override."),
24605
- patch: OpEntry.default(true).describe("PATCH partial-update endpoint. true = enabled, false = disabled, object = external URI or security override."),
24606
- delete: OpEntry.default(true).describe("DELETE endpoint. true = enabled, false = disabled, object = external URI or security override.")
24607
- });
24608
-
24609
24571
  // ../crouton-core/src/lib/resource/ColumnType.schema.ts
24610
24572
  var ColumnTypeShorthandSchema = external_exports.enum([
24611
24573
  "string",
@@ -25112,6 +25074,22 @@ var parseFieldInputOptions = (fi) => {
25112
25074
  };
25113
25075
 
25114
25076
  // ../crouton-core/src/lib/resource/Column.ts
25077
+ var ColumnRelationSchema = external_exports.object({
25078
+ resource: external_exports.string().describe(
25079
+ 'Relative path to the child resource definition (e.g. "./author.resource"). Resolved at load time.'
25080
+ ),
25081
+ type: RelationType.optional().describe(
25082
+ "Cardinality of the relation. Auto-derived from the Zod model or a sibling FK column when omitted."
25083
+ ),
25084
+ foreignKey: external_exports.string().optional().describe(
25085
+ 'FK field on the child model pointing back to the parent. Defaults to "${parentModel}Id".'
25086
+ ),
25087
+ prismaField: external_exports.string().optional().describe(
25088
+ "Override the Prisma relation field name when it differs from the column id."
25089
+ ),
25090
+ sort: external_exports.string().optional().describe("Field to sort related records by"),
25091
+ sortDir: external_exports.enum(["asc", "desc"]).optional().describe("Sort direction for related records")
25092
+ });
25115
25093
  var UniqueConfigSchema = external_exports.object({
25116
25094
  scope: external_exports.array(external_exports.string()).optional().describe(
25117
25095
  "Other column ids the uniqueness is scoped by (composite unique constraint)."
@@ -25124,7 +25102,7 @@ var UniqueConfigSchema = external_exports.object({
25124
25102
  )
25125
25103
  });
25126
25104
  var UniqueSchema = external_exports.union([external_exports.boolean(), UniqueConfigSchema]);
25127
- var JsonColumnSchema = external_exports.object({
25105
+ var JsonColumnBaseSchema = external_exports.object({
25128
25106
  id: external_exports.string().describe(
25129
25107
  'Column identifier. In map form this is the map key; in array form it must be explicit. Must match the Prisma model field name exactly, unless overridden by "column".'
25130
25108
  ),
@@ -25166,9 +25144,8 @@ var JsonColumnSchema = external_exports.object({
25166
25144
  idField: external_exports.boolean().default(false).describe(
25167
25145
  "Marks this column as the primary-key field. Exactly one column per resource should set this to true."
25168
25146
  ),
25169
- showInLookup: external_exports.boolean().default(false).describe(
25170
- "When true, this column appears in autocomplete/lookup dropdowns referencing this resource."
25171
- ),
25147
+ /** @deprecated Use resource-level `lookup.labels` instead (run `crouton migrate`). Kept as passthrough so the codemod can read it. */
25148
+ showInLookup: external_exports.boolean().optional(),
25172
25149
  /**
25173
25150
  * Enforce that this column's value is unique across the resource. `true` is
25174
25151
  * simple single-column uniqueness; the object form allows a composite
@@ -25217,6 +25194,9 @@ var JsonColumnSchema = external_exports.object({
25217
25194
  required: external_exports.boolean().optional().describe(
25218
25195
  'Whether the form requires a value. On a "prisma" resource this overrides the Prisma-derived required state; on "custom" resources it is the only way to mark a field required. Applies to forms only.'
25219
25196
  ),
25197
+ relation: ColumnRelationSchema.optional().describe(
25198
+ "Sub-resource relation configuration. Prefer this over fieldInput.resource / fieldInput.relationType for new resources."
25199
+ ),
25220
25200
  fieldInput: FieldInputSchema.optional().describe(
25221
25201
  'Configures the UI control used in create/edit forms and the display format. Use "type" to pick the input widget; "format" to pick a special renderer (e.g. "relation").'
25222
25202
  ),
@@ -25235,10 +25215,89 @@ var JsonColumnSchema = external_exports.object({
25235
25215
  ),
25236
25216
  columns: external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), external_exports.unknown())).optional().describe(
25237
25217
  'Per-sub-column overrides when using "extend". Key is the virtual column id ("{extendId}_{refColId}") or the referenced column id. Value is any JsonColumn fields to merge.'
25238
- )
25218
+ ),
25219
+ /**
25220
+ * Marks this as a computed column backed by a raw SQL subquery.
25221
+ * Replaces the legacy top-level `calculatedColumns` array (run `crouton migrate`).
25222
+ */
25223
+ computed: external_exports.object({
25224
+ sql: external_exports.string().describe('Raw SQL subquery. Use "main" as the alias for the parent table row.'),
25225
+ type: external_exports.enum(["number", "boolean", "string"]).optional().describe('Cast type for the result. Defaults to "number".')
25226
+ }).optional().describe("SQL computed column. The column value is derived from a raw subquery at query time.")
25227
+ });
25228
+ var JsonColumnSchema = JsonColumnBaseSchema.superRefine((col, ctx) => {
25229
+ if (col.enum && col.fieldInput?.options?.["values"]) {
25230
+ ctx.addIssue({
25231
+ code: "custom",
25232
+ path: ["fieldInput", "options", "values"],
25233
+ message: "Column has both 'enum' and 'fieldInput.options.values'; use 'enum' for shared lists or 'values' on fieldInput for local lists \u2014 not both."
25234
+ });
25235
+ }
25236
+ });
25237
+
25238
+ // ../crouton-core/src/lib/data-source/Security.schema.ts
25239
+ var SecuritySchema = external_exports.union([
25240
+ external_exports.object({ public: external_exports.literal(true).describe("When true, the route is public and requires no authentication.") }),
25241
+ external_exports.object({ guard: external_exports.union([external_exports.string(), external_exports.array(external_exports.string()).min(1)]).describe("Named guard(s) that must all pass (AND logic). A single string or an array of guard names.") })
25242
+ ]);
25243
+
25244
+ // ../crouton-core/src/lib/resource/include.schema.ts
25245
+ var JsonIncludeEntrySchema = external_exports.lazy(
25246
+ () => external_exports.union([
25247
+ external_exports.string(),
25248
+ // plain relation name → `{ [name]: true }`
25249
+ external_exports.object({
25250
+ relation: external_exports.string(),
25251
+ // required
25252
+ include: external_exports.array(JsonIncludeEntrySchema).optional(),
25253
+ // nested includes;
25254
+ /** Prisma-format orderBy clause applied to the included records. */
25255
+ orderBy: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
25256
+ })
25257
+ ])
25258
+ );
25259
+
25260
+ // ../crouton-core/src/lib/data-source/Operations.schema.ts
25261
+ var ExternalOpEntry = external_exports.object({
25262
+ uri: external_exports.string().describe("Full URL of the external API endpoint that handles this operation."),
25263
+ method: external_exports.string().optional().describe("HTTP method for the external endpoint. Defaults to the standard method for each operation."),
25264
+ security: SecuritySchema.optional().describe("Security override for this specific external operation.")
25265
+ });
25266
+ var IncludeField = {
25267
+ include: external_exports.array(JsonIncludeEntrySchema).optional().describe("Prisma relations to eager-load for this operation.")
25268
+ };
25269
+ var OpEntry = external_exports.union([
25270
+ external_exports.boolean(),
25271
+ ExternalOpEntry,
25272
+ external_exports.object({
25273
+ security: SecuritySchema.optional().describe("Security override for this specific operation. Overrides the resource-level global security block."),
25274
+ ...IncludeField
25275
+ })
25276
+ ]);
25277
+ var FindAllOpEntry = external_exports.union([
25278
+ external_exports.boolean(),
25279
+ ExternalOpEntry.extend({
25280
+ search: external_exports.array(external_exports.string()).optional().describe("Column ids (or dot paths) that ?q= matches."),
25281
+ filter: external_exports.array(external_exports.string()).optional().describe("Optional allowlist of filter fields the server accepts."),
25282
+ ...IncludeField
25283
+ }),
25284
+ external_exports.object({
25285
+ security: SecuritySchema.optional().describe("Security override for this specific operation. Overrides the resource-level global security block."),
25286
+ search: external_exports.array(external_exports.string()).optional().describe("Column ids (or dot paths) that ?q= matches."),
25287
+ filter: external_exports.array(external_exports.string()).optional().describe("Optional allowlist of filter fields the server accepts."),
25288
+ ...IncludeField
25289
+ })
25290
+ ]);
25291
+ var JsonOperationsSchema = external_exports.object({
25292
+ findAll: FindAllOpEntry.default(true).describe("GET collection endpoint. true = enabled, false = disabled, object = external URI or security override."),
25293
+ findOne: OpEntry.default(true).describe("GET single record endpoint. true = enabled, false = disabled, object = external URI or security override."),
25294
+ create: OpEntry.default(true).describe("POST create endpoint. true = enabled, false = disabled, object = external URI or security override."),
25295
+ update: OpEntry.default(true).describe("PUT full-update endpoint. true = enabled, false = disabled, object = external URI or security override."),
25296
+ patch: OpEntry.default(true).describe("PATCH partial-update endpoint. true = enabled, false = disabled, object = external URI or security override."),
25297
+ delete: OpEntry.default(true).describe("DELETE endpoint. true = enabled, false = disabled, object = external URI or security override.")
25239
25298
  });
25240
25299
 
25241
- // ../crouton-core/src/lib/resource/layout/LayoutItem.schema.ts
25300
+ // ../crouton-core/src/lib/layout/schema/LayoutItem.schema.ts
25242
25301
  var WhenConditionSchema = external_exports.object({
25243
25302
  field: external_exports.string().describe(
25244
25303
  "The other column id whose value is evaluated for the condition."
@@ -25253,14 +25312,14 @@ var WhenConditionSchema = external_exports.object({
25253
25312
  notExists: external_exports.boolean().optional().describe("When true, show/hide/disable when the field has no value.")
25254
25313
  });
25255
25314
  var _LayoutFieldControlSchema = external_exports.object({
25256
- id: external_exports.string(),
25257
- colspan: external_exports.number().int().min(1).max(12).optional(),
25258
- rowspan: external_exports.number().int().min(1).max(6).optional(),
25259
- width: external_exports.string().optional(),
25260
- label: external_exports.string().optional(),
25261
- hideLabel: external_exports.boolean().optional(),
25262
- type: external_exports.string().optional(),
25263
- options: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
25315
+ id: external_exports.string().describe("Column id this control is bound to."),
25316
+ colspan: external_exports.number().int().min(1).max(12).default(12).describe("Number of grid columns this control spans (1\u201312). Defaults to full width."),
25317
+ rowspan: external_exports.number().int().min(1).max(6).default(1).describe("Number of grid rows this control spans (1\u20136). Defaults to 1."),
25318
+ width: external_exports.string().default("full").describe('Tailwind width class applied to the input wrapper (e.g. "w-48", "full"). Defaults to full width.'),
25319
+ label: external_exports.string().optional().describe("Override the column label shown above the input."),
25320
+ hideLabel: external_exports.boolean().default(false).optional().describe("Hide the label above the input."),
25321
+ type: external_exports.string().optional().describe('Override the field input type (e.g. "text", "select").'),
25322
+ options: external_exports.record(external_exports.string(), external_exports.unknown()).optional().describe("Field input options passed to the renderer."),
25264
25323
  showWhen: WhenConditionSchema.optional().describe(
25265
25324
  "Conditionally show this control when the condition matches another field value."
25266
25325
  ),
@@ -25276,18 +25335,18 @@ var LayoutFieldItemSchema = external_exports.union([
25276
25335
  _LayoutFieldControlSchema
25277
25336
  ]);
25278
25337
  var LayoutComponentItemSchema = external_exports.object({
25279
- component: external_exports.string(),
25280
- colspan: external_exports.number().int().min(1).max(12).optional(),
25281
- rowspan: external_exports.number().int().min(1).max(6).optional(),
25282
- width: external_exports.string().optional(),
25283
- label: external_exports.string().optional()
25338
+ component: external_exports.string().describe("Name of the Vue component to render in this slot."),
25339
+ colspan: external_exports.number().int().min(1).max(12).default(12).describe("Number of grid columns this slot spans (1\u201312). Defaults to full width."),
25340
+ rowspan: external_exports.number().int().min(1).max(6).default(1).describe("Number of grid rows this slot spans (1\u20136). Defaults to 1."),
25341
+ width: external_exports.string().default("full").describe("Tailwind width class applied to the slot wrapper. Defaults to full width."),
25342
+ label: external_exports.string().optional().describe("Optional label rendered above the component slot.")
25284
25343
  });
25285
25344
  var LayoutControlSchema = external_exports.union([
25286
25345
  LayoutFieldItemSchema,
25287
25346
  LayoutComponentItemSchema
25288
25347
  ]);
25289
25348
 
25290
- // ../crouton-core/src/lib/resource/layout/LayoutNode.schema.ts
25349
+ // ../crouton-core/src/lib/layout/schema/LayoutNode.schema.ts
25291
25350
  var FormLayoutNodeSchema = external_exports.lazy(
25292
25351
  () => external_exports.object({
25293
25352
  type: external_exports.enum(["grid", "vertical", "horizontal", "collapse", "group"]).optional().describe(
@@ -25343,13 +25402,34 @@ var transformAction = (action) => {
25343
25402
  };
25344
25403
  var JsonActionSchema = external_exports.union([JsonProcedureActionSchema, JsonLinkActionSchema]).transform(transformAction);
25345
25404
 
25346
- // ../crouton-core/src/lib/resource/layout/TableLayout.schema.ts
25347
- var LayoutTableFieldControlSchema = _LayoutFieldControlSchema.extend({
25348
- sortable: external_exports.boolean().default(false).describe("Enables a sort toggle on the table column header."),
25349
- defaultSort: external_exports.boolean().default(false).describe(
25350
- "When true, the table is sorted by this column by default. At most one column should set this."
25351
- )
25352
- });
25405
+ // ../crouton-core/src/lib/layout/schema/TableLayout.schema.ts
25406
+ var TABLE_REMOVED_KEYS = ["colspan", "rowspan", "hideLabel"];
25407
+ var LayoutTableFieldControlSchema = external_exports.object({ id: external_exports.string() }).passthrough().transform((val, ctx) => {
25408
+ for (const key of TABLE_REMOVED_KEYS) {
25409
+ if (val[key] !== void 0) {
25410
+ ctx.addIssue({
25411
+ code: "custom",
25412
+ path: [key],
25413
+ message: `'${key}' is not valid on table items; remove it`
25414
+ });
25415
+ }
25416
+ }
25417
+ return val;
25418
+ }).pipe(
25419
+ _LayoutFieldControlSchema.omit({ colspan: true, rowspan: true, hideLabel: true }).extend({
25420
+ sortable: external_exports.boolean().default(false).describe("Enables a sort toggle on the table column header."),
25421
+ width: external_exports.union([external_exports.enum(["xs", "sm", "md", "lg", "xl"]), external_exports.string()]).optional().describe('Column width: named size (xs\u2013xl, same scale as form) or a CSS value like "12rem".'),
25422
+ minWidth: external_exports.string().default("8rem").describe("Minimum column width as a CSS value."),
25423
+ maxWidth: external_exports.string().default("24rem").describe("Maximum column width as a CSS value."),
25424
+ align: external_exports.enum(["left", "center", "right"]).optional().describe("Horizontal text alignment. Defaults to right for number columns, left otherwise."),
25425
+ truncate: external_exports.union([external_exports.boolean(), external_exports.number().int().positive()]).default(true).describe("Truncate long text with ellipsis. true = 1-line clamp; number = line count."),
25426
+ pin: external_exports.enum(["left", "right"]).optional().describe("Stick this column to the left or right edge of the table."),
25427
+ visible: external_exports.boolean().default(true).describe(
25428
+ "false = hidden initially but available in column chooser."
25429
+ ),
25430
+ headerTooltip: external_exports.string().optional().describe("Help text shown on the column header.")
25431
+ })
25432
+ );
25353
25433
  var TableItemSchema = external_exports.union([
25354
25434
  external_exports.string(),
25355
25435
  LayoutTableFieldControlSchema,
@@ -25361,11 +25441,25 @@ var TableNodeSchema = external_exports.object({
25361
25441
  title: external_exports.string().optional().describe("Static heading shown above the table."),
25362
25442
  label: external_exports.string().optional().describe("Alias for title; title takes precedence."),
25363
25443
  filterFields: external_exports.array(FilterFields).optional().default([]),
25444
+ defaultSort: external_exports.object({
25445
+ field: external_exports.string().default("id").describe("Column id to sort by. Defaults to `id`."),
25446
+ sortDir: external_exports.enum(["asc", "desc"]).default("asc").describe("Sort direction. Default: asc.")
25447
+ }).optional().describe("Default sort applied on initial table load. Omit to use the first sortable column."),
25364
25448
  items: external_exports.array(TableItemSchema).optional().describe(
25365
25449
  "Ordered list of columns. Each entry is a column id string, a field control config, or a component cell."
25366
25450
  ),
25367
- rowActions: external_exports.array(JsonActionSchema).default([]).describe("Per-record row actions shown in the table. Each action receives the record id."),
25368
- tableActions: external_exports.array(JsonActionSchema).default([]).describe("Global table-level actions shown as toolbar buttons (no record id).")
25451
+ rowActions: external_exports.array(JsonActionSchema).default([]).describe(
25452
+ "Per-record row actions shown in the table. Each action receives the record id."
25453
+ ),
25454
+ tableActions: external_exports.array(JsonActionSchema).default([]).describe(
25455
+ "Global table-level actions shown as toolbar buttons (no record id)."
25456
+ ),
25457
+ pageSize: external_exports.number().int().positive().default(20).describe("Default page size. Server may cap it."),
25458
+ pageSizes: external_exports.array(external_exports.number().int().positive()).default([20, 50, 100]).describe("Page-size options shown in the selector (e.g. [20, 50, 100])."),
25459
+ density: external_exports.enum(["normal", "compact"]).optional().default("normal").describe("Row density. Default: normal."),
25460
+ selectable: external_exports.boolean().optional().describe("Show row checkboxes for multi-select."),
25461
+ rowClick: external_exports.enum(["view", "edit", "none"]).default("view").describe("Action when a table row is clicked."),
25462
+ emptyText: external_exports.string().optional().describe("Message shown when the table has no rows.")
25369
25463
  });
25370
25464
  var TableWrapperNodeSchema = external_exports.lazy(
25371
25465
  () => external_exports.object({
@@ -25421,12 +25515,31 @@ var TableLayoutRootSchema = external_exports.union([TableNodeSchema, TableWrappe
25421
25515
  }
25422
25516
  });
25423
25517
 
25424
- // ../crouton-core/src/lib/resource/layout/Layout.schema.ts
25518
+ // ../crouton-core/src/lib/layout/schema/Layout.schema.ts
25519
+ var ContainerSchema = external_exports.object({
25520
+ mode: external_exports.enum(["modal", "page"]).default("modal").describe(
25521
+ 'How this context opens: "modal" (default) shows a dialog; "page" navigates to a route.'
25522
+ ),
25523
+ size: external_exports.enum(["xs", "sm", "lg", "xl"]).default("sm").describe(
25524
+ 'Width of the modal dialog. Only applies when mode is "modal". Default: "sm".'
25525
+ )
25526
+ });
25527
+ var FormLayoutWithContainerSchema = FormLayoutNodeSchema.and(
25528
+ external_exports.object({ container: ContainerSchema.default({ mode: "modal", size: "sm" }) })
25529
+ );
25530
+ var ViewLayoutWithContainerSchema = FormLayoutNodeSchema.and(
25531
+ external_exports.object({
25532
+ container: ContainerSchema.default({ mode: "modal", size: "sm" }),
25533
+ component: external_exports.string().optional().describe(
25534
+ "Import path to a custom Vue component for the detail view. When set, the default view layout is ignored."
25535
+ )
25536
+ })
25537
+ );
25425
25538
  var LayoutSchema = external_exports.object({
25426
- form: FormLayoutNodeSchema.optional().describe(
25539
+ form: FormLayoutWithContainerSchema.optional().describe(
25427
25540
  "Explicit layout for the create/edit form. When absent, columns are rendered in source order in a grid."
25428
25541
  ),
25429
- view: FormLayoutNodeSchema.optional().describe(
25542
+ view: ViewLayoutWithContainerSchema.optional().describe(
25430
25543
  "Explicit layout for the read-only detail view. When absent, columns are rendered in source order."
25431
25544
  ),
25432
25545
  table: TableLayoutRootSchema.optional().describe(
@@ -25434,7 +25547,7 @@ var LayoutSchema = external_exports.object({
25434
25547
  )
25435
25548
  });
25436
25549
 
25437
- // ../crouton-core/src/lib/view/view.schema.ts
25550
+ // ../crouton-core/src/lib/schema/view/view.schema.ts
25438
25551
  var ViewColumnConfigSchema = external_exports.object({
25439
25552
  id: external_exports.string(),
25440
25553
  label: external_exports.string().optional(),
@@ -25450,7 +25563,8 @@ var ViewConfigSchema = external_exports.object({
25450
25563
  ui: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
25451
25564
  data: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
25452
25565
  columns: external_exports.array(ViewColumnConfigSchema),
25453
- defaultSort: external_exports.string().optional()
25566
+ defaultSort: external_exports.string().optional(),
25567
+ defaultSortDir: external_exports.enum(["asc", "desc"]).optional()
25454
25568
  });
25455
25569
  var viewDefList = ["table", "view", "form"];
25456
25570
  var ViewDefEnum = external_exports.enum(viewDefList);
@@ -25505,30 +25619,24 @@ var SidebarSchema = external_exports.object({
25505
25619
  var registry2 = /* @__PURE__ */ new Map();
25506
25620
  var getResourceExtensions = () => registry2;
25507
25621
 
25508
- // ../crouton-core/src/lib/resource/include.schema.ts
25509
- var JsonIncludeEntrySchema = external_exports.lazy(
25510
- () => external_exports.union([
25511
- external_exports.string(),
25512
- // plain relation name → `{ [name]: true }`
25513
- external_exports.object({
25514
- relation: external_exports.string(),
25515
- // required
25516
- include: external_exports.array(JsonIncludeEntrySchema).optional(),
25517
- // nested includes;
25518
- /** Prisma-format orderBy clause applied to the included records. */
25519
- orderBy: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
25520
- })
25521
- ])
25522
- );
25523
-
25524
25622
  // ../crouton-core/src/lib/resource/version.ts
25525
25623
  var BASELINE_RESOURCE_VERSION = 1;
25526
25624
  var CURRENT_RESOURCE_VERSION = 1;
25527
25625
 
25528
25626
  // ../crouton-core/src/lib/resource/ResourceJson.schema.ts
25627
+ var ROOT_REMOVED_KEYS = {
25628
+ display: "'display' moved to layout.form.container / layout.view.container (run crouton migrate)",
25629
+ modalSize: "'modalSize' moved to layout.form.container.size (run crouton migrate)",
25630
+ include: "'include' moved to operations.findAll.include / operations.findOne.include (run crouton migrate)",
25631
+ model: "'model' moved to source.model (run crouton migrate)",
25632
+ table: "'table' moved to source.dbTable (run crouton migrate)",
25633
+ database: "'database' moved to source.datasource (run crouton migrate)",
25634
+ idType: "'idType' moved to source.idType (run crouton migrate)",
25635
+ calculatedColumns: "'calculatedColumns' moved to columns[id].computed (run crouton migrate)"
25636
+ };
25529
25637
  var JsonColumnsMapSchema = external_exports.record(
25530
25638
  external_exports.string(),
25531
- JsonColumnSchema.omit({ id: true }).catchall(external_exports.unknown())
25639
+ JsonColumnBaseSchema.omit({ id: true }).catchall(external_exports.unknown())
25532
25640
  );
25533
25641
  var ColumnsSchema = JsonColumnsMapSchema;
25534
25642
  var JsonDisplaySchema = external_exports.object({
@@ -25586,8 +25694,12 @@ var ResourceJsonShape = external_exports.object({
25586
25694
  'Prisma model name. Required when kind is "prisma"; must be absent when kind is "custom".'
25587
25695
  ),
25588
25696
  tag: external_exports.string().optional().default("Crouton").describe(
25589
- 'OpenAPI tag grouping for generated endpoints. Defaults to "Crouton".'
25697
+ 'OpenAPI tag grouping for generated endpoints. Defaults to "Crouton". Prefer openapi.tag for new resources.'
25590
25698
  ),
25699
+ openapi: external_exports.object({
25700
+ tag: external_exports.string().optional().describe("OpenAPI tag for grouping generated endpoints."),
25701
+ description: external_exports.string().optional().describe("Description shown in the OpenAPI spec for this tag group.")
25702
+ }).optional().describe("OpenAPI metadata for generated endpoints. Supersedes the root-level tag field."),
25591
25703
  title: external_exports.string().optional().describe(
25592
25704
  'Human-readable title shown in the UI sidebar and page headers. Defaults to a title-cased version of "name".'
25593
25705
  ),
@@ -25610,9 +25722,6 @@ var ResourceJsonShape = external_exports.object({
25610
25722
  sidebar: SidebarSchema.default(SidebarSchema.parse({})).describe(
25611
25723
  "Sidebar visibility and ordering config. Default: shown, alphabetically ordered, ungrouped."
25612
25724
  ),
25613
- display: JsonDisplaySchema.default(JsonDisplaySchema.parse({})).describe(
25614
- "How records open when clicked. Default: modal dialog."
25615
- ),
25616
25725
  security: SecuritySchema.optional().describe(
25617
25726
  "Global security block applied to every operation unless overridden per-operation. Use per-operation security in the operations block for finer control."
25618
25727
  ),
@@ -25625,12 +25734,19 @@ var ResourceJsonShape = external_exports.object({
25625
25734
  calculatedColumns: external_exports.array(CalculatedColumnSchema).default([]).describe(
25626
25735
  'Columns computed from raw SQL expressions. Not supported on "custom" resources.'
25627
25736
  ),
25628
- modalSize: external_exports.enum(["xs", "sm", "lg", "xl"]).default("sm").describe(
25629
- 'Width of the create/edit modal dialog. "xs", "sm" (default), "lg", or "xl".'
25630
- ),
25631
25737
  include: external_exports.array(JsonIncludeEntrySchema).default([]).describe(
25632
25738
  'Prisma relations to include when querying this resource. Plain string: "author" \u2192 include: { author: true }. Object: { relation: "textAuthor", include: ["author"] } for nested includes.'
25633
25739
  ),
25740
+ source: external_exports.object({
25741
+ model: external_exports.string().optional().describe("Prisma model name."),
25742
+ dbTable: external_exports.string().optional().describe("Database table name override."),
25743
+ datasource: external_exports.string().optional().describe("Datasource name."),
25744
+ idType: external_exports.enum(["string", "number"]).optional().describe("Primary key type.")
25745
+ }).optional().describe("Where the data comes from (replaces root model/table/database/idType)."),
25746
+ lookup: external_exports.object({
25747
+ labels: external_exports.array(external_exports.string()).describe("Ordered display fields for relation dropdowns."),
25748
+ separator: external_exports.string().optional().default(" ")
25749
+ }).optional().describe("How this resource is labelled in other resources' dropdowns."),
25634
25750
  layout: LayoutSchema.optional().describe(
25635
25751
  "Explicit layout for generated views (form, view, table). When absent, source-order grid is used."
25636
25752
  )
@@ -25644,6 +25760,13 @@ var refineByKind = (obj, ctx) => {
25644
25760
  message: 'A custom resource has no Prisma model. Remove "model" \u2014 data access comes from repository.ts.'
25645
25761
  });
25646
25762
  }
25763
+ if (obj.source?.model !== void 0) {
25764
+ ctx.addIssue({
25765
+ code: "custom",
25766
+ path: ["source", "model"],
25767
+ message: 'A custom resource has no Prisma model. Remove "source.model" \u2014 data access comes from repository.ts.'
25768
+ });
25769
+ }
25647
25770
  if (obj.calculatedColumns?.length) {
25648
25771
  ctx.addIssue({
25649
25772
  code: "custom",
@@ -25677,6 +25800,17 @@ var refineByKind = (obj, ctx) => {
25677
25800
  message: '"parent" is only supported on a custom resource. A prisma resource is nested by declaring a relation column on its parent.'
25678
25801
  });
25679
25802
  }
25803
+ for (const [id, col] of Object.entries(obj.columns ?? {})) {
25804
+ const hasEnum = !!col.enum;
25805
+ const hasValues = !!col.fieldInput?.options?.values?.length;
25806
+ if (hasEnum && hasValues) {
25807
+ ctx.addIssue({
25808
+ code: "custom",
25809
+ path: ["columns", id],
25810
+ message: `Column "${id}" has both 'enum' and 'fieldInput.options.values'; use 'enum' for shared lists or 'values' on fieldInput for local lists, not both.`
25811
+ });
25812
+ }
25813
+ }
25680
25814
  };
25681
25815
  var collectFormLayoutIds = (node2) => {
25682
25816
  const ids = [];
@@ -25836,10 +25970,12 @@ var buildResourceJsonSchema = () => {
25836
25970
  );
25837
25971
  const shape = ext.size ? ResourceJsonShape.extend(extShape) : ResourceJsonShape;
25838
25972
  let _rawLayoutErrors = [];
25973
+ let _rawRootErrors = [];
25839
25974
  return external_exports.preprocess(
25840
25975
  (raw) => {
25841
25976
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return raw;
25842
25977
  let obj = raw;
25978
+ _rawRootErrors = Object.entries(ROOT_REMOVED_KEYS).filter(([k]) => k in obj).map(([k, message]) => ({ path: [k], message }));
25843
25979
  _rawLayoutErrors = collectRemovedLayoutKeyErrors(obj["layout"], ["layout"]);
25844
25980
  {
25845
25981
  const layout = obj["layout"];
@@ -26025,7 +26161,7 @@ var buildResourceJsonSchema = () => {
26025
26161
  return obj;
26026
26162
  },
26027
26163
  shape.superRefine(refineByKind).superRefine((_, ctx) => {
26028
- for (const err of _rawLayoutErrors) {
26164
+ for (const err of [..._rawRootErrors, ..._rawLayoutErrors]) {
26029
26165
  ctx.addIssue({ code: "custom", path: err.path, message: err.message });
26030
26166
  }
26031
26167
  }).superRefine(refineLayout).transform((obj) => {
@@ -26131,7 +26267,7 @@ var CompiledSubResourceConfigSchema = external_exports.object({
26131
26267
  relationType: external_exports.enum(["oneToMany", "manyToOne"]).optional()
26132
26268
  });
26133
26269
 
26134
- // ../crouton-core/src/lib/compile/schema-input.ts
26270
+ // ../crouton-core/src/lib/schema/json/schema-input.ts
26135
26271
  var JsonSchemaInputSchema = external_exports.object({
26136
26272
  type: external_exports.literal("object"),
26137
26273
  properties: external_exports.record(external_exports.string(), external_exports.unknown()),
@@ -26143,7 +26279,7 @@ var SchemaInputSchema = external_exports.union([
26143
26279
  JsonSchemaInputSchema
26144
26280
  ]);
26145
26281
 
26146
- // ../crouton-core/src/lib/compile/definition.schema.ts
26282
+ // ../crouton-core/src/lib/schema/definition/definition.schema.ts
26147
26283
  var ExternalOperationDefSchema = external_exports.object({
26148
26284
  uri: external_exports.string(),
26149
26285
  method: external_exports.string().optional()
@@ -26173,7 +26309,7 @@ var DefinitionSchema = external_exports.union([
26173
26309
  DefinitionCallbackSchema
26174
26310
  ]);
26175
26311
 
26176
- // ../crouton-core/src/lib/compile/lookup.schema.ts
26312
+ // ../crouton-core/src/lib/schema/definition/lookup.schema.ts
26177
26313
  var LookupSchema = external_exports.object({
26178
26314
  key: external_exports.string(),
26179
26315
  label: external_exports.string().optional(),
@@ -26190,7 +26326,21 @@ var CompiledResourceSchema = ResourceJsonShape.extend({
26190
26326
  views: external_exports.record(external_exports.string(), ViewConfigSchema),
26191
26327
  definition: DefinitionSchema,
26192
26328
  idField: external_exports.string(),
26193
- idType: external_exports.enum(["string", "number"]).default("string")
26329
+ // Derived from layout.form.container / layout.view.container at compile time (Block 2)
26330
+ display: JsonDisplaySchema.optional(),
26331
+ modalSize: external_exports.enum(["xs", "sm", "lg", "xl"]).optional(),
26332
+ /** Eager-loaded relations for findAll (list) queries. From operations.findAll.include. */
26333
+ include: external_exports.array(JsonIncludeEntrySchema).optional(),
26334
+ /** Eager-loaded relations for findOne (detail) queries. From operations.findOne.include. */
26335
+ findOneInclude: external_exports.array(JsonIncludeEntrySchema).optional(),
26336
+ /** Prisma model name (flattened from source.model). */
26337
+ model: external_exports.string().optional(),
26338
+ /** Database table name override (flattened from source.table). */
26339
+ table: external_exports.string().optional(),
26340
+ /** Datasource name (flattened from source.database). */
26341
+ database: external_exports.string().optional(),
26342
+ /** Primary key type (flattened from source.idType). */
26343
+ idType: external_exports.enum(["string", "number"]).optional()
26194
26344
  });
26195
26345
 
26196
26346
  // ../crouton-core/src/lib/config/CroutonConfig.schema.ts
@@ -26265,7 +26415,7 @@ var TranslationBundleSchema = external_exports.object({
26265
26415
  var RESOURCE_SCHEMA_URL = `https://ghentcdh.github.io/crouton/schema/v${CURRENT_RESOURCE_VERSION}/resource.schema.json`;
26266
26416
  var FIELD_INPUT_SCHEMA_BASE = `https://ghentcdh.github.io/crouton/schema/v${CURRENT_RESOURCE_VERSION}`;
26267
26417
 
26268
- // ../crouton-core/src/lib/response/action.schema.ts
26418
+ // ../crouton-core/src/lib/schema/response/action.schema.ts
26269
26419
  var ActionConditionSchema2 = external_exports.object({
26270
26420
  field: external_exports.string(),
26271
26421
  op: external_exports.enum(["eq", "neq", "gt", "gte", "lt", "lte", "exists", "notExists"]).optional().default("eq"),
@@ -26298,7 +26448,7 @@ var TableActionSchema = external_exports.union([
26298
26448
  ProcedureActionSchema.omit({ condition: true })
26299
26449
  ]);
26300
26450
 
26301
- // ../crouton-core/src/lib/response/operation.schema.ts
26451
+ // ../crouton-core/src/lib/schema/response/operation.schema.ts
26302
26452
  var MethodSchema = external_exports.enum([
26303
26453
  "get",
26304
26454
  "post",
@@ -26344,7 +26494,7 @@ var OperationSuffixMap = {
26344
26494
  patch: "/{id}"
26345
26495
  };
26346
26496
 
26347
- // ../crouton-core/src/lib/response/view.schema.ts
26497
+ // ../crouton-core/src/lib/schema/response/view.schema.ts
26348
26498
  var ViewSchemaZ = external_exports.object({
26349
26499
  data: external_exports.any(),
26350
26500
  ui: external_exports.any()
@@ -26360,7 +26510,7 @@ var Display = external_exports.object({
26360
26510
  customComponent: external_exports.string().nullish()
26361
26511
  });
26362
26512
 
26363
- // ../crouton-core/src/lib/response/resource-definition.schema.ts
26513
+ // ../crouton-core/src/lib/schema/response/resource-definition.schema.ts
26364
26514
  var ResourceDefinitionResponseZ = external_exports.object({
26365
26515
  id: external_exports.string(),
26366
26516
  name: external_exports.string(),
@@ -26750,7 +26900,7 @@ var runCreate = async (name, opts) => {
26750
26900
  const tokens = {
26751
26901
  name,
26752
26902
  Name: toTitle(name),
26753
- version: true ? "0.0.1-alpha.86" : "latest",
26903
+ version: true ? "0.0.1-alpha.87" : "latest",
26754
26904
  year: String((/* @__PURE__ */ new Date()).getFullYear()),
26755
26905
  pmRun: pm === "npm" ? "npm run" : pm,
26756
26906
  urlEnv: "DATABASE_URL",