@svadmin/create 0.18.1 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5023,10 +5023,10 @@ var require_picocolors = __commonJS(function(exports, module) {
5023
5023
  });
5024
5024
 
5025
5025
  // src/index.ts
5026
- var import_prompts = __toESM(require_prompts3(), 1);
5027
- var import_picocolors = __toESM(require_picocolors(), 1);
5028
- import fs from "node:fs";
5029
- import path from "node:path";
5026
+ var import_prompts2 = __toESM(require_prompts3(), 1);
5027
+ var import_picocolors2 = __toESM(require_picocolors(), 1);
5028
+ import fs3 from "node:fs";
5029
+ import path3 from "node:path";
5030
5030
  import { fileURLToPath } from "node:url";
5031
5031
  import { createRequire as createRequire2 } from "node:module";
5032
5032
  import { spawnSync } from "node:child_process";
@@ -5127,6 +5127,1571 @@ function createProjectPackageJson(scaffold, options) {
5127
5127
  };
5128
5128
  }
5129
5129
 
5130
+ // src/infer-command.ts
5131
+ var import_picocolors = __toESM(require_picocolors(), 1);
5132
+ var import_prompts = __toESM(require_prompts3(), 1);
5133
+ import fs from "node:fs";
5134
+ import path from "node:path";
5135
+
5136
+ // ../core/src/inferencer-openapi.ts
5137
+ function isRef(obj) {
5138
+ return obj !== null && typeof obj === "object" && "$ref" in obj && typeof obj.$ref === "string";
5139
+ }
5140
+ function resolveRef(ref, root) {
5141
+ const parts = ref.replace("#/", "").split("/");
5142
+ let current = root;
5143
+ for (const part of parts) {
5144
+ if (current === null || typeof current !== "object")
5145
+ return null;
5146
+ current = current[part];
5147
+ if (!current)
5148
+ return null;
5149
+ }
5150
+ return current;
5151
+ }
5152
+ function resolveSchema(schemaOrRef, root) {
5153
+ if (isRef(schemaOrRef)) {
5154
+ return resolveRef(schemaOrRef.$ref, root);
5155
+ }
5156
+ return schemaOrRef;
5157
+ }
5158
+ function mapOpenAPITypeToFieldType(key, schema, root) {
5159
+ const { type, format } = schema;
5160
+ if (schema.enum && schema.enum.length > 0)
5161
+ return "select";
5162
+ if (type === "array") {
5163
+ if (schema.items) {
5164
+ const itemSchema = resolveSchema(schema.items, root);
5165
+ if (itemSchema?.type === "string") {
5166
+ const lk = key.toLowerCase();
5167
+ if (lk.includes("image") || lk.includes("photo"))
5168
+ return "images";
5169
+ return "tags";
5170
+ }
5171
+ }
5172
+ return "tags";
5173
+ }
5174
+ if (type === "object")
5175
+ return "json";
5176
+ if (type === "boolean")
5177
+ return "boolean";
5178
+ if (type === "number" || type === "integer")
5179
+ return "number";
5180
+ if (type === "string") {
5181
+ if (format === "date" || format === "date-time")
5182
+ return "date";
5183
+ if (format === "email")
5184
+ return "email";
5185
+ if (format === "uri" || format === "url")
5186
+ return "url";
5187
+ if (format === "binary" || format === "byte")
5188
+ return "image";
5189
+ const lk = key.toLowerCase();
5190
+ if (lk.includes("email"))
5191
+ return "email";
5192
+ if (lk.includes("phone") || lk.includes("tel"))
5193
+ return "phone";
5194
+ if (lk.includes("url") || lk.includes("link") || lk.includes("website"))
5195
+ return "url";
5196
+ if (lk.includes("avatar") || lk.includes("image") || lk.includes("photo") || lk.includes("logo"))
5197
+ return "image";
5198
+ if (lk.includes("color") || lk.includes("colour"))
5199
+ return "color";
5200
+ if (lk.includes("description") || lk.includes("content") || lk.includes("body") || lk.includes("bio"))
5201
+ return "textarea";
5202
+ if (lk === "created_at" || lk === "updated_at" || lk.endsWith("_at") || lk.endsWith("_date"))
5203
+ return "date";
5204
+ return "text";
5205
+ }
5206
+ return "text";
5207
+ }
5208
+ function inferFromOpenAPI(spec, options = {}) {
5209
+ const { primaryKey = "id", include, exclude } = options;
5210
+ const schemas = spec.components?.schemas ?? {};
5211
+ const resources = [];
5212
+ const resourcePaths = detectResourcePaths(spec);
5213
+ for (const [schemaName, schemaObj] of Object.entries(schemas)) {
5214
+ if (include && !include.includes(schemaName))
5215
+ continue;
5216
+ if (exclude && exclude.includes(schemaName))
5217
+ continue;
5218
+ const resolved = resolveSchema(schemaObj, spec);
5219
+ if (!resolved || resolved.type !== "object" || !resolved.properties)
5220
+ continue;
5221
+ const fields = [];
5222
+ const requiredFields = new Set(resolved.required ?? []);
5223
+ for (const [propName, propSchemaOrRef] of Object.entries(resolved.properties)) {
5224
+ const propSchema = resolveSchema(propSchemaOrRef, spec);
5225
+ if (!propSchema)
5226
+ continue;
5227
+ const fieldType = mapOpenAPITypeToFieldType(propName, propSchema, spec);
5228
+ let relatedResource = null;
5229
+ if (isRef(propSchemaOrRef)) {
5230
+ const refName = propSchemaOrRef.$ref.split("/").pop() ?? "";
5231
+ relatedResource = refName.toLowerCase() + "s";
5232
+ } else if (propName.endsWith("_id") || propName.endsWith("Id")) {
5233
+ const base = propName.replace(/_id$|Id$/, "");
5234
+ relatedResource = base.endsWith("s") ? base : base + "s";
5235
+ }
5236
+ const field = {
5237
+ key: propName,
5238
+ label: humanize(propName),
5239
+ type: relatedResource ? "relation" : fieldType,
5240
+ required: requiredFields.has(propName),
5241
+ sortable: ["text", "number", "date"].includes(fieldType),
5242
+ searchable: ["text", "email"].includes(fieldType),
5243
+ showInList: propName !== primaryKey && fieldType !== "textarea" && fieldType !== "json",
5244
+ showInForm: propName !== primaryKey
5245
+ };
5246
+ if (relatedResource) {
5247
+ field.resource = relatedResource;
5248
+ field.optionLabel = "name";
5249
+ field.optionValue = "id";
5250
+ }
5251
+ if (propSchema.enum) {
5252
+ field.options = propSchema.enum.map((v) => ({
5253
+ label: String(v).charAt(0).toUpperCase() + String(v).slice(1),
5254
+ value: v
5255
+ }));
5256
+ }
5257
+ fields.push(field);
5258
+ }
5259
+ fields.sort((a, b) => {
5260
+ if (a.key === primaryKey)
5261
+ return -1;
5262
+ if (b.key === primaryKey)
5263
+ return 1;
5264
+ return 0;
5265
+ });
5266
+ const baseName = schemaName.toLowerCase();
5267
+ const resourceName = baseName.endsWith("s") ? baseName : baseName + "s";
5268
+ const pathInfo = resourcePaths.get(baseName) ?? resourcePaths.get(resourceName);
5269
+ resources.push({
5270
+ name: resourceName,
5271
+ label: schemaName,
5272
+ primaryKey,
5273
+ fields,
5274
+ canCreate: pathInfo?.hasPost ?? true,
5275
+ canEdit: pathInfo?.hasPut ?? true,
5276
+ canDelete: pathInfo?.hasDelete ?? true,
5277
+ canShow: pathInfo?.hasGet ?? true
5278
+ });
5279
+ }
5280
+ return resources;
5281
+ }
5282
+ function detectResourcePaths(spec) {
5283
+ const result = new Map;
5284
+ if (!spec.paths)
5285
+ return result;
5286
+ for (const [path, methods] of Object.entries(spec.paths)) {
5287
+ const segments = path.split("/").filter(Boolean);
5288
+ const resourceSegment = segments.find((s) => !s.startsWith("{") && s !== "api" && s !== "v1" && s !== "v2");
5289
+ if (!resourceSegment)
5290
+ continue;
5291
+ const name = resourceSegment.toLowerCase();
5292
+ const existing = result.get(name) ?? { hasGet: false, hasPost: false, hasPut: false, hasDelete: false };
5293
+ if (methods.get)
5294
+ existing.hasGet = true;
5295
+ if (methods.post)
5296
+ existing.hasPost = true;
5297
+ if (methods.put || methods.patch)
5298
+ existing.hasPut = true;
5299
+ if (methods.delete)
5300
+ existing.hasDelete = true;
5301
+ result.set(name, existing);
5302
+ }
5303
+ return result;
5304
+ }
5305
+ function humanize(key) {
5306
+ return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
5307
+ }
5308
+ // ../core/src/inferencer-graphql.ts
5309
+ var GRAPHQL_INTROSPECTION_QUERY = `query IntrospectionQuery {
5310
+ __schema {
5311
+ queryType { name }
5312
+ mutationType { name }
5313
+ subscriptionType { name }
5314
+ types {
5315
+ kind
5316
+ name
5317
+ description
5318
+ fields(includeDeprecated: true) {
5319
+ name
5320
+ description
5321
+ type {
5322
+ kind
5323
+ name
5324
+ ofType {
5325
+ kind
5326
+ name
5327
+ ofType {
5328
+ kind
5329
+ name
5330
+ ofType {
5331
+ kind
5332
+ name
5333
+ ofType {
5334
+ kind
5335
+ name
5336
+ }
5337
+ }
5338
+ }
5339
+ }
5340
+ }
5341
+ }
5342
+ enumValues(includeDeprecated: true) {
5343
+ name
5344
+ description
5345
+ }
5346
+ }
5347
+ }
5348
+ }`;
5349
+ function unwrapType(typeRef) {
5350
+ let current = typeRef;
5351
+ let isRequired = false;
5352
+ let isList = false;
5353
+ if (current.kind === "NON_NULL") {
5354
+ isRequired = true;
5355
+ if (current.ofType)
5356
+ current = current.ofType;
5357
+ }
5358
+ if (current.kind === "LIST") {
5359
+ isList = true;
5360
+ if (current.ofType) {
5361
+ current = current.ofType;
5362
+ if (current.kind === "NON_NULL") {
5363
+ if (current.ofType)
5364
+ current = current.ofType;
5365
+ }
5366
+ }
5367
+ }
5368
+ return {
5369
+ baseName: current.name ?? "String",
5370
+ kind: current.kind,
5371
+ isList,
5372
+ isRequired
5373
+ };
5374
+ }
5375
+ function mapGraphQLTypeToFieldType(key, typeInfo, enumMap) {
5376
+ const { baseName, isList } = typeInfo;
5377
+ const lk = key.toLowerCase();
5378
+ if (enumMap.has(baseName))
5379
+ return "select";
5380
+ if (isList) {
5381
+ if (lk.includes("image") || lk.includes("photo") || lk.includes("avatar") || lk.includes("picture")) {
5382
+ return "images";
5383
+ }
5384
+ return "tags";
5385
+ }
5386
+ switch (baseName) {
5387
+ case "Int":
5388
+ case "Float":
5389
+ case "Decimal":
5390
+ case "BigInt":
5391
+ return "number";
5392
+ case "Boolean":
5393
+ return "boolean";
5394
+ case "Date":
5395
+ case "DateTime":
5396
+ case "Time":
5397
+ case "Timestamp":
5398
+ return "date";
5399
+ case "JSON":
5400
+ case "JsonObject":
5401
+ case "JSONObject":
5402
+ case "Json":
5403
+ return "json";
5404
+ case "ID":
5405
+ return "text";
5406
+ case "String":
5407
+ default:
5408
+ break;
5409
+ }
5410
+ if (lk.includes("email"))
5411
+ return "email";
5412
+ if (lk.includes("phone") || lk.includes("tel") || lk.includes("mobile"))
5413
+ return "phone";
5414
+ if (lk.includes("url") || lk.includes("link") || lk.includes("website"))
5415
+ return "url";
5416
+ if (lk.includes("avatar") || lk.includes("image") || lk.includes("photo") || lk.includes("thumbnail") || lk.includes("logo"))
5417
+ return "image";
5418
+ if (lk.includes("color") || lk.includes("colour"))
5419
+ return "color";
5420
+ if (lk.includes("description") || lk.includes("content") || lk.includes("body") || lk.includes("bio") || lk.includes("summary"))
5421
+ return "textarea";
5422
+ if (lk === "created_at" || lk === "updated_at" || lk.endsWith("_at") || lk.endsWith("_date") || /\bdate\b/.test(lk))
5423
+ return "date";
5424
+ return "text";
5425
+ }
5426
+ function parseGraphQLSDL(sdl) {
5427
+ const types = [];
5428
+ const queryType = { name: "Query" };
5429
+ const mutationType = { name: "Mutation" };
5430
+ const cleanSDL = sdl.replace(/#[^\n\r]*/g, "");
5431
+ const enumRegex = /enum\s+([A-Za-z0-9_]+)\s*\{([^}]+)\}/g;
5432
+ let match;
5433
+ while ((match = enumRegex.exec(cleanSDL)) !== null) {
5434
+ const enumName = match[1];
5435
+ const rawValues = match[2].split(/\s+/).filter(Boolean);
5436
+ types.push({
5437
+ kind: "ENUM",
5438
+ name: enumName,
5439
+ enumValues: rawValues.map((v) => ({ name: v }))
5440
+ });
5441
+ }
5442
+ const typeRegex = /type\s+([A-Za-z0-9_]+)\s*(?:implements\s+[A-Za-z0-9_&,\s]+)?\s*\{([^}]+)\}/g;
5443
+ while ((match = typeRegex.exec(cleanSDL)) !== null) {
5444
+ const typeName = match[1];
5445
+ const fieldsBody = match[2];
5446
+ const fieldLines = fieldsBody.split(`
5447
+ `).map((l) => l.trim()).filter(Boolean);
5448
+ const fields = [];
5449
+ for (const line of fieldLines) {
5450
+ const fieldMatch = /^([A-Za-z0-9_]+)(?:\([^)]*\))?\s*:\s*([[\]A-Za-z0-9_!]+)/.exec(line);
5451
+ if (!fieldMatch)
5452
+ continue;
5453
+ const fName = fieldMatch[1];
5454
+ const rawType = fieldMatch[2];
5455
+ const isNonNullable = rawType.endsWith("!");
5456
+ const cleanType = isNonNullable ? rawType.slice(0, -1) : rawType;
5457
+ const isList = cleanType.startsWith("[") && cleanType.endsWith("]");
5458
+ const innerType = isList ? cleanType.slice(1, -1).replace(/!$/, "") : cleanType;
5459
+ let typeRef = {
5460
+ kind: "SCALAR",
5461
+ name: innerType
5462
+ };
5463
+ if (isList) {
5464
+ typeRef = {
5465
+ kind: "LIST",
5466
+ ofType: typeRef
5467
+ };
5468
+ }
5469
+ if (isNonNullable) {
5470
+ typeRef = {
5471
+ kind: "NON_NULL",
5472
+ ofType: typeRef
5473
+ };
5474
+ }
5475
+ fields.push({
5476
+ name: fName,
5477
+ type: typeRef
5478
+ });
5479
+ }
5480
+ types.push({
5481
+ kind: "OBJECT",
5482
+ name: typeName,
5483
+ fields
5484
+ });
5485
+ }
5486
+ return {
5487
+ queryType,
5488
+ mutationType,
5489
+ types
5490
+ };
5491
+ }
5492
+ function inferFromGraphQL(schemaOrIntrospection, options = {}) {
5493
+ const { primaryKey = "id", include, exclude } = options;
5494
+ let schemaBody;
5495
+ if (typeof schemaOrIntrospection === "string") {
5496
+ schemaBody = parseGraphQLSDL(schemaOrIntrospection);
5497
+ } else if (schemaOrIntrospection && typeof schemaOrIntrospection === "object" && "data" in schemaOrIntrospection && schemaOrIntrospection.data?.__schema) {
5498
+ schemaBody = schemaOrIntrospection.data.__schema;
5499
+ } else if (schemaOrIntrospection && typeof schemaOrIntrospection === "object" && "__schema" in schemaOrIntrospection && schemaOrIntrospection.__schema) {
5500
+ schemaBody = schemaOrIntrospection.__schema;
5501
+ } else if (schemaOrIntrospection && typeof schemaOrIntrospection === "object" && "types" in schemaOrIntrospection && Array.isArray(schemaOrIntrospection.types)) {
5502
+ schemaBody = schemaOrIntrospection;
5503
+ } else {
5504
+ return [];
5505
+ }
5506
+ const types = schemaBody.types ?? [];
5507
+ const queryTypeName = schemaBody.queryType?.name ?? "Query";
5508
+ const mutationTypeName = schemaBody.mutationType?.name ?? "Mutation";
5509
+ const subscriptionTypeName = schemaBody.subscriptionType?.name ?? "Subscription";
5510
+ const enumMap = new Map;
5511
+ for (const t of types) {
5512
+ if (t.kind === "ENUM" && t.name) {
5513
+ const values = (t.enumValues ?? []).map((ev) => ev.name);
5514
+ enumMap.set(t.name, values);
5515
+ }
5516
+ }
5517
+ const objectTypeMap = new Map;
5518
+ for (const t of types) {
5519
+ if (t.kind === "OBJECT" && t.name) {
5520
+ objectTypeMap.set(t.name, t);
5521
+ }
5522
+ }
5523
+ const queryFields = new Set;
5524
+ const queryType = objectTypeMap.get(queryTypeName);
5525
+ if (queryType?.fields) {
5526
+ for (const f of queryType.fields)
5527
+ queryFields.add(f.name.toLowerCase());
5528
+ }
5529
+ const mutationFields = new Set;
5530
+ const mutationType = objectTypeMap.get(mutationTypeName);
5531
+ if (mutationType?.fields) {
5532
+ for (const f of mutationType.fields)
5533
+ mutationFields.add(f.name.toLowerCase());
5534
+ }
5535
+ const resources = [];
5536
+ for (const typeObj of types) {
5537
+ const typeName = typeObj.name;
5538
+ if (!typeName)
5539
+ continue;
5540
+ if (typeName.startsWith("__") || typeName === queryTypeName || typeName === mutationTypeName || typeName === subscriptionTypeName || typeObj.kind !== "OBJECT" || !typeObj.fields || typeObj.fields.length === 0) {
5541
+ continue;
5542
+ }
5543
+ if (include && !include.includes(typeName))
5544
+ continue;
5545
+ if (exclude && exclude.includes(typeName))
5546
+ continue;
5547
+ const baseName = typeName.toLowerCase();
5548
+ const resourceName = baseName.endsWith("s") ? baseName : baseName + "s";
5549
+ const fields = [];
5550
+ for (const fieldDesc of typeObj.fields) {
5551
+ const fieldName = fieldDesc.name;
5552
+ const typeInfo = unwrapType(fieldDesc.type);
5553
+ let fieldType = mapGraphQLTypeToFieldType(fieldName, typeInfo, enumMap);
5554
+ let relatedResource = null;
5555
+ if (objectTypeMap.has(typeInfo.baseName) && typeInfo.baseName !== typeName) {
5556
+ const target = typeInfo.baseName.toLowerCase();
5557
+ relatedResource = target.endsWith("s") ? target : target + "s";
5558
+ fieldType = "relation";
5559
+ } else if (fieldName.endsWith("_id") || fieldName.endsWith("Id")) {
5560
+ const base = fieldName.replace(/_id$|Id$/, "");
5561
+ relatedResource = base.endsWith("s") ? base : base + "s";
5562
+ fieldType = "relation";
5563
+ }
5564
+ const field = {
5565
+ key: fieldName,
5566
+ label: humanize2(fieldName),
5567
+ type: relatedResource ? "relation" : fieldType,
5568
+ required: typeInfo.isRequired,
5569
+ sortable: ["text", "number", "date"].includes(fieldType),
5570
+ searchable: ["text", "email"].includes(fieldType),
5571
+ showInList: fieldName !== primaryKey && fieldType !== "textarea" && fieldType !== "json",
5572
+ showInForm: fieldName !== primaryKey
5573
+ };
5574
+ if (relatedResource) {
5575
+ field.resource = relatedResource;
5576
+ field.optionLabel = "name";
5577
+ field.optionValue = "id";
5578
+ }
5579
+ if (enumMap.has(typeInfo.baseName)) {
5580
+ const enumValues = enumMap.get(typeInfo.baseName) ?? [];
5581
+ field.options = enumValues.map((val) => ({
5582
+ label: humanize2(val),
5583
+ value: val
5584
+ }));
5585
+ }
5586
+ fields.push(field);
5587
+ }
5588
+ fields.sort((a, b) => {
5589
+ if (a.key === primaryKey)
5590
+ return -1;
5591
+ if (b.key === primaryKey)
5592
+ return 1;
5593
+ return a.key.localeCompare(b.key);
5594
+ });
5595
+ const canShow = queryFields.size === 0 || queryFields.has(baseName) || queryFields.has(resourceName) || queryFields.has(`get${baseName}`) || queryFields.has(`${baseName}byid`) || queryFields.has(`find${baseName}`);
5596
+ const canCreate = mutationFields.size === 0 || mutationFields.has(`create${baseName}`) || mutationFields.has(`insert${baseName}`) || mutationFields.has(`add${baseName}`) || mutationFields.has(`insert_${resourceName}`) || mutationFields.has(`create_${baseName}`);
5597
+ const canEdit = mutationFields.size === 0 || mutationFields.has(`update${baseName}`) || mutationFields.has(`edit${baseName}`) || mutationFields.has(`update_${resourceName}`) || mutationFields.has(`update_${baseName}`) || mutationFields.has(`save${baseName}`);
5598
+ const canDelete = mutationFields.size === 0 || mutationFields.has(`delete${baseName}`) || mutationFields.has(`remove${baseName}`) || mutationFields.has(`delete_${resourceName}`) || mutationFields.has(`delete_${baseName}`);
5599
+ resources.push({
5600
+ name: resourceName,
5601
+ label: typeName,
5602
+ primaryKey,
5603
+ fields,
5604
+ canCreate,
5605
+ canEdit,
5606
+ canDelete,
5607
+ canShow
5608
+ });
5609
+ }
5610
+ return resources;
5611
+ }
5612
+ function humanize2(key) {
5613
+ return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
5614
+ }
5615
+
5616
+ // ../core/src/inferencer.ts
5617
+ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
5618
+ var URL_RE = /^https?:\/\//;
5619
+ var IMAGE_RE = /\.(png|jpe?g|gif|svg|webp|avif|ico)(\?.*)?$/i;
5620
+ var PHONE_RE = /^\+?[\d\s\-()]{7,}$/;
5621
+ var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2})?/;
5622
+ var COLOR_RE = /^#([0-9a-fA-F]{3,8})$/;
5623
+ function inferFieldType(key, value) {
5624
+ if (value === null || value === undefined)
5625
+ return "text";
5626
+ if (typeof value === "boolean")
5627
+ return "boolean";
5628
+ if (typeof value === "number")
5629
+ return "number";
5630
+ if (Array.isArray(value)) {
5631
+ if (value.length > 0 && typeof value[0] === "string") {
5632
+ if (value.every((v) => typeof v === "string" && IMAGE_RE.test(v)))
5633
+ return "images";
5634
+ return "tags";
5635
+ }
5636
+ if (value.length > 0 && typeof value[0] === "number")
5637
+ return "tags";
5638
+ return "json";
5639
+ }
5640
+ if (typeof value === "object")
5641
+ return "json";
5642
+ if (typeof value === "string") {
5643
+ if (COLOR_RE.test(value))
5644
+ return "color";
5645
+ if (EMAIL_RE.test(value))
5646
+ return "email";
5647
+ if (IMAGE_RE.test(value))
5648
+ return "image";
5649
+ if (URL_RE.test(value))
5650
+ return "url";
5651
+ if (PHONE_RE.test(value))
5652
+ return "phone";
5653
+ if (ISO_DATE_RE.test(value))
5654
+ return "date";
5655
+ if (value.length > 200)
5656
+ return "textarea";
5657
+ const lk = key.toLowerCase();
5658
+ if (lk.includes("email"))
5659
+ return "email";
5660
+ if (lk.includes("phone") || lk.includes("tel") || lk.includes("mobile"))
5661
+ return "phone";
5662
+ if (lk.includes("url") || lk.includes("link") || lk.includes("website"))
5663
+ return "url";
5664
+ if (lk.includes("avatar") || lk.includes("image") || lk.includes("photo") || lk.includes("thumbnail") || lk.includes("logo"))
5665
+ return "image";
5666
+ if (lk.includes("color") || lk.includes("colour"))
5667
+ return "color";
5668
+ if (lk.includes("description") || lk.includes("content") || lk.includes("body") || lk.includes("bio") || lk.includes("summary"))
5669
+ return "textarea";
5670
+ if (lk === "created_at" || lk === "updated_at" || lk.endsWith("_at") || lk.endsWith("_date") || /\bdate\b/.test(lk))
5671
+ return "date";
5672
+ return "text";
5673
+ }
5674
+ return "text";
5675
+ }
5676
+ var RELATION_SUFFIXES = ["_id", "Id", "_ID"];
5677
+ function isLikelyRelation(key) {
5678
+ for (const suffix of RELATION_SUFFIXES) {
5679
+ if (key.endsWith(suffix)) {
5680
+ const resource = key.slice(0, -suffix.length);
5681
+ return resource.endsWith("s") ? resource : resource + "s";
5682
+ }
5683
+ }
5684
+ return null;
5685
+ }
5686
+ function inferResource(resourceName, sampleData, options = {}) {
5687
+ const primaryKey = options.primaryKey ?? "id";
5688
+ const label = options.label ?? capitalize(resourceName);
5689
+ if (!sampleData.length) {
5690
+ const emptyResource = { name: resourceName, label, fields: [], primaryKey };
5691
+ return {
5692
+ fields: [],
5693
+ resource: emptyResource,
5694
+ code: `// No data available to infer fields for "${resourceName}".`,
5695
+ typeboxCode: generateTypeBoxSchemaCode(emptyResource),
5696
+ componentCode: {
5697
+ list: generateListPageCode(emptyResource),
5698
+ create: generateCreatePageCode(emptyResource),
5699
+ edit: generateEditPageCode(emptyResource),
5700
+ show: generateShowPageCode(emptyResource)
5701
+ }
5702
+ };
5703
+ }
5704
+ const keySet = new Set;
5705
+ for (const row of sampleData) {
5706
+ for (const k of Object.keys(row))
5707
+ keySet.add(k);
5708
+ }
5709
+ const fields = [];
5710
+ for (const key of keySet) {
5711
+ const values = sampleData.map((r) => r[key]).filter((v) => v !== null && v !== undefined);
5712
+ const sampleValue = values[0];
5713
+ let inferredType = inferFieldType(key, sampleValue);
5714
+ const typeCounts = new Map;
5715
+ for (const v of values) {
5716
+ const t = inferFieldType(key, v);
5717
+ typeCounts.set(t, (typeCounts.get(t) ?? 0) + 1);
5718
+ }
5719
+ let maxCount = 0;
5720
+ for (const [t, count] of typeCounts) {
5721
+ if (count > maxCount) {
5722
+ maxCount = count;
5723
+ inferredType = t;
5724
+ }
5725
+ }
5726
+ const relatedResource = isLikelyRelation(key);
5727
+ const uniqueStrings = new Set(values.filter((v) => typeof v === "string"));
5728
+ const isSelect = inferredType === "text" && uniqueStrings.size > 1 && uniqueStrings.size <= 10 && values.length >= 5;
5729
+ const field = {
5730
+ key,
5731
+ label: humanize3(key),
5732
+ type: relatedResource ? "relation" : isSelect ? "select" : inferredType,
5733
+ sortable: inferredType === "text" || inferredType === "number" || inferredType === "date",
5734
+ searchable: inferredType === "text" || inferredType === "email",
5735
+ showInList: key !== primaryKey && inferredType !== "textarea" && inferredType !== "json" && inferredType !== "richtext",
5736
+ showInForm: key !== primaryKey
5737
+ };
5738
+ if (relatedResource) {
5739
+ field.resource = relatedResource;
5740
+ field.optionLabel = "name";
5741
+ field.optionValue = "id";
5742
+ }
5743
+ if (isSelect) {
5744
+ field.options = [...uniqueStrings].map((v) => ({ label: capitalize(v), value: v }));
5745
+ }
5746
+ fields.push(field);
5747
+ }
5748
+ fields.sort((a, b) => {
5749
+ if (a.key === primaryKey)
5750
+ return -1;
5751
+ if (b.key === primaryKey)
5752
+ return 1;
5753
+ return a.key.localeCompare(b.key);
5754
+ });
5755
+ const resource = {
5756
+ name: resourceName,
5757
+ label,
5758
+ primaryKey,
5759
+ fields,
5760
+ canCreate: true,
5761
+ canEdit: true,
5762
+ canDelete: true,
5763
+ canShow: true
5764
+ };
5765
+ const code = generateCode(resource);
5766
+ const typeboxCode = generateTypeBoxSchemaCode(resource);
5767
+ const componentCode = {
5768
+ list: generateListPageCode(resource),
5769
+ create: generateCreatePageCode(resource),
5770
+ edit: generateEditPageCode(resource),
5771
+ show: generateShowPageCode(resource)
5772
+ };
5773
+ return { fields, resource, code, typeboxCode, componentCode };
5774
+ }
5775
+ function generateCode(resource) {
5776
+ const esc = (s) => s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
5777
+ const fieldsCode = resource.fields.map((f) => {
5778
+ const lines = [
5779
+ ` { key: '${esc(f.key)}', label: '${esc(f.label)}', type: '${f.type}'`
5780
+ ];
5781
+ if (f.sortable)
5782
+ lines.push(` sortable: true`);
5783
+ if (f.searchable)
5784
+ lines.push(` searchable: true`);
5785
+ if (f.showInList === false)
5786
+ lines.push(` showInList: false`);
5787
+ if (f.showInForm === false)
5788
+ lines.push(` showInForm: false`);
5789
+ if (f.resource) {
5790
+ lines.push(` resource: '${f.resource}'`);
5791
+ lines.push(` optionLabel: '${f.optionLabel}'`);
5792
+ lines.push(` optionValue: '${f.optionValue}'`);
5793
+ }
5794
+ if (f.options) {
5795
+ lines.push(` options: ${JSON.stringify(f.options)}`);
5796
+ }
5797
+ return lines.join(`,
5798
+ `) + " }";
5799
+ }).join(`,
5800
+ `);
5801
+ return `import type { ResourceDefinition } from '@svadmin/core';
5802
+
5803
+ export const ${resource.name}Resource: ResourceDefinition = {
5804
+ name: '${resource.name}',
5805
+ label: '${resource.label}',
5806
+ primaryKey: '${resource.primaryKey ?? "id"}',
5807
+ canCreate: true,
5808
+ canEdit: true,
5809
+ canDelete: true,
5810
+ canShow: true,
5811
+ fields: [
5812
+ ${fieldsCode}
5813
+ ],
5814
+ };
5815
+ `;
5816
+ }
5817
+ function generateListPageCode(resource) {
5818
+ return `<script lang="ts">
5819
+ import { ListPage, AutoTable } from '@svadmin/ui';
5820
+ </script>
5821
+
5822
+ <ListPage resourceName="${resource.name}">
5823
+ <AutoTable resourceName="${resource.name}" />
5824
+ </ListPage>
5825
+ `;
5826
+ }
5827
+ function generateCreatePageCode(resource) {
5828
+ return `<script lang="ts">
5829
+ import { CreatePage, AutoForm } from '@svadmin/ui';
5830
+ </script>
5831
+
5832
+ <CreatePage resourceName="${resource.name}">
5833
+ <AutoForm resourceName="${resource.name}" mode="create" />
5834
+ </CreatePage>
5835
+ `;
5836
+ }
5837
+ function generateEditPageCode(resource) {
5838
+ return `<script lang="ts">
5839
+ import { EditPage, AutoForm } from '@svadmin/ui';
5840
+
5841
+ interface Props {
5842
+ id?: string | number;
5843
+ }
5844
+
5845
+ let { id }: Props = $props();
5846
+ </script>
5847
+
5848
+ <EditPage resourceName="${resource.name}" {id}>
5849
+ <AutoForm resourceName="${resource.name}" {id} mode="edit" />
5850
+ </EditPage>
5851
+ `;
5852
+ }
5853
+ function generateShowPageCode(resource) {
5854
+ return `<script lang="ts">
5855
+ import { ShowPage, AutoForm } from '@svadmin/ui';
5856
+
5857
+ interface Props {
5858
+ id?: string | number;
5859
+ }
5860
+
5861
+ let { id }: Props = $props();
5862
+ </script>
5863
+
5864
+ <ShowPage resourceName="${resource.name}" {id}>
5865
+ <AutoForm resourceName="${resource.name}" {id} mode="show" />
5866
+ </ShowPage>
5867
+ `;
5868
+ }
5869
+ function generateResourceCode(resource) {
5870
+ return generateCode(resource);
5871
+ }
5872
+ function generateTypeBoxSchemaCode(resource) {
5873
+ const esc = (s) => s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
5874
+ const schemaProps = resource.fields.map((f) => {
5875
+ let typeDef;
5876
+ const isRequired = f.required === true;
5877
+ switch (f.type) {
5878
+ case "number":
5879
+ typeDef = "Type.Number()";
5880
+ break;
5881
+ case "boolean":
5882
+ typeDef = "Type.Boolean()";
5883
+ break;
5884
+ case "date":
5885
+ typeDef = "Type.String({ format: 'date-time' })";
5886
+ break;
5887
+ case "email":
5888
+ typeDef = "Type.String({ format: 'email' })";
5889
+ break;
5890
+ case "url":
5891
+ typeDef = "Type.String({ format: 'uri' })";
5892
+ break;
5893
+ case "phone":
5894
+ typeDef = "Type.String({ pattern: '^\\\\+?[\\\\d\\\\s\\\\-()]{7,}$' })";
5895
+ break;
5896
+ case "tags":
5897
+ typeDef = "Type.Array(Type.String())";
5898
+ break;
5899
+ case "images":
5900
+ typeDef = "Type.Array(Type.String({ format: 'uri' }))";
5901
+ break;
5902
+ case "json":
5903
+ typeDef = "Type.Record(Type.String(), Type.Unknown())";
5904
+ break;
5905
+ case "select":
5906
+ if (f.options && f.options.length > 0) {
5907
+ const literals = f.options.map((o) => `Type.Literal('${esc(String(o.value))}')`).join(", ");
5908
+ typeDef = `Type.Union([${literals}])`;
5909
+ } else {
5910
+ typeDef = "Type.String()";
5911
+ }
5912
+ break;
5913
+ case "relation":
5914
+ typeDef = "Type.Union([Type.String(), Type.Number()])";
5915
+ break;
5916
+ case "textarea":
5917
+ case "richtext":
5918
+ case "text":
5919
+ default:
5920
+ typeDef = "Type.String()";
5921
+ break;
5922
+ }
5923
+ if (!isRequired && f.key !== (resource.primaryKey ?? "id")) {
5924
+ typeDef = `Type.Optional(${typeDef})`;
5925
+ }
5926
+ return ` ${f.key}: ${typeDef},`;
5927
+ }).join(`
5928
+ `);
5929
+ const baseName = resource.name.endsWith("s") ? resource.name.slice(0, -1) : resource.name;
5930
+ const typeName = capitalize(baseName);
5931
+ return `import { Type, type Static } from '@sinclair/typebox';
5932
+
5933
+ export const ${typeName}Schema = Type.Object({
5934
+ ${schemaProps}
5935
+ });
5936
+
5937
+ export type ${typeName} = Static<typeof ${typeName}Schema>;
5938
+ `;
5939
+ }
5940
+ function capitalize(s) {
5941
+ return s.charAt(0).toUpperCase() + s.slice(1);
5942
+ }
5943
+ function humanize3(key) {
5944
+ return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
5945
+ }
5946
+ function generateResourceBundle(resource) {
5947
+ return {
5948
+ fields: resource.fields,
5949
+ resource,
5950
+ code: generateResourceCode(resource),
5951
+ typeboxCode: generateTypeBoxSchemaCode(resource),
5952
+ componentCode: {
5953
+ list: generateListPageCode(resource),
5954
+ create: generateCreatePageCode(resource),
5955
+ edit: generateEditPageCode(resource),
5956
+ show: generateShowPageCode(resource)
5957
+ }
5958
+ };
5959
+ }
5960
+
5961
+ // src/infer-command.ts
5962
+ function parseInferArguments(args) {
5963
+ const options = {
5964
+ headers: {},
5965
+ primaryKey: "id",
5966
+ method: "GET",
5967
+ format: "all"
5968
+ };
5969
+ for (let i = 0;i < args.length; i++) {
5970
+ const arg = args[i];
5971
+ if (arg === "--write" || arg === "-w") {
5972
+ options.write = true;
5973
+ } else if (arg === "--dry-run") {
5974
+ options.write = false;
5975
+ } else if (arg === "--url" || arg === "-u") {
5976
+ options.url = args[++i];
5977
+ } else if (arg.startsWith("--url=")) {
5978
+ options.url = arg.slice(6);
5979
+ } else if (arg === "--file" || arg === "-f") {
5980
+ options.file = args[++i];
5981
+ } else if (arg.startsWith("--file=")) {
5982
+ options.file = arg.slice(7);
5983
+ } else if (arg === "--type" || arg === "-t") {
5984
+ options.type = args[++i];
5985
+ } else if (arg.startsWith("--type=")) {
5986
+ options.type = arg.slice(7);
5987
+ } else if (arg === "--resource" || arg === "-r") {
5988
+ options.resource = args[++i];
5989
+ } else if (arg.startsWith("--resource=")) {
5990
+ options.resource = arg.slice(11);
5991
+ } else if (arg === "--out-dir" || arg === "-o" || arg === "--output") {
5992
+ options.outDir = args[++i];
5993
+ } else if (arg.startsWith("--out-dir=")) {
5994
+ options.outDir = arg.slice(10);
5995
+ } else if (arg.startsWith("--output=")) {
5996
+ options.outDir = arg.slice(9);
5997
+ } else if (arg === "--primary-key" || arg === "-k") {
5998
+ options.primaryKey = args[++i];
5999
+ } else if (arg.startsWith("--primary-key=")) {
6000
+ options.primaryKey = arg.slice(14);
6001
+ } else if (arg === "--header" || arg === "-H") {
6002
+ const headerLine = args[++i] ?? "";
6003
+ const colonIndex = headerLine.indexOf(":");
6004
+ if (colonIndex > 0) {
6005
+ const key = headerLine.slice(0, colonIndex).trim();
6006
+ const value = headerLine.slice(colonIndex + 1).trim();
6007
+ if (!options.headers)
6008
+ options.headers = {};
6009
+ options.headers[key] = value;
6010
+ }
6011
+ } else if (arg.startsWith("--header=")) {
6012
+ const headerLine = arg.slice(9);
6013
+ const colonIndex = headerLine.indexOf(":");
6014
+ if (colonIndex > 0) {
6015
+ const key = headerLine.slice(0, colonIndex).trim();
6016
+ const value = headerLine.slice(colonIndex + 1).trim();
6017
+ if (!options.headers)
6018
+ options.headers = {};
6019
+ options.headers[key] = value;
6020
+ }
6021
+ } else if (arg === "--method" || arg === "-m") {
6022
+ options.method = (args[++i] ?? "GET").toUpperCase();
6023
+ } else if (arg.startsWith("--method=")) {
6024
+ options.method = arg.slice(9).toUpperCase();
6025
+ } else if (arg === "--body" || arg === "-b") {
6026
+ options.body = args[++i];
6027
+ } else if (arg.startsWith("--body=")) {
6028
+ options.body = arg.slice(7);
6029
+ } else if (arg === "--format") {
6030
+ options.format = args[++i];
6031
+ } else if (arg.startsWith("--format=")) {
6032
+ options.format = arg.slice(9);
6033
+ } else if (arg === "--help" || arg === "-h") {
6034
+ printInferHelp();
6035
+ process.exit(0);
6036
+ } else if (!arg.startsWith("-") && !options.url && !options.file) {
6037
+ if (arg.startsWith("http://") || arg.startsWith("https://")) {
6038
+ options.url = arg;
6039
+ } else if (fs.existsSync(arg)) {
6040
+ options.file = arg;
6041
+ } else {
6042
+ options.resource = arg;
6043
+ }
6044
+ }
6045
+ }
6046
+ return options;
6047
+ }
6048
+ function printInferHelp() {
6049
+ console.log(`
6050
+ ${import_picocolors.default.bold("svadmin infer")} — Automatically infer ResourceDefinitions, TypeBox Schemas, and Svelte 5 CRUD components from REST/GraphQL APIs or OpenAPI specs.
6051
+
6052
+ ${import_picocolors.default.bold("USAGE:")}
6053
+ svadmin infer [OPTIONS]
6054
+ svadmin infer <url|file> [OPTIONS]
6055
+
6056
+ ${import_picocolors.default.bold("OPTIONS:")}
6057
+ -u, --url <url> REST API endpoint, OpenAPI schema URL, or GraphQL endpoint
6058
+ -f, --file <path> Local OpenAPI JSON/YAML, GraphQL schema (.graphql/JSON), or sample JSON data file
6059
+ -t, --type <type> Explicit source type: rest | openapi | graphql | auto (default: auto)
6060
+ -r, --resource <name> Target resource name filter or explicit resource name for sample data
6061
+ -o, --out-dir <dir> Target directory for generated code (e.g. src/resources)
6062
+ -k, --primary-key <key> Primary key field name (default: id)
6063
+ -H, --header <key:value> Custom HTTP header for fetch requests (can be specified multiple times)
6064
+ -m, --method <GET|POST> HTTP method for REST fetch (default: GET)
6065
+ -b, --body <json> HTTP request body for POST requests
6066
+ -w, --write Write generated files to target directory (default is dry-run when --out-dir is set)
6067
+ --format <all|resource|typebox|components>
6068
+ Output format (default: all)
6069
+ -h, --help Show this help message
6070
+
6071
+ ${import_picocolors.default.bold("EXAMPLES:")}
6072
+ svadmin infer --url https://api.example.com/openapi.json --out-dir src/resources --write
6073
+ svadmin infer --url https://api.example.com/graphql --out-dir src/resources --write
6074
+ svadmin infer --url https://api.example.com/api/v1/posts --resource posts
6075
+ svadmin infer --file schema.graphql --out-dir src/resources --write
6076
+ svadmin infer --file sample-posts.json --resource posts --out-dir src/resources --write
6077
+ `);
6078
+ }
6079
+ async function loadSourceData(options, customFetch = fetch) {
6080
+ if (options.file) {
6081
+ const filePath = path.resolve(process.cwd(), options.file);
6082
+ if (!fs.existsSync(filePath)) {
6083
+ throw new Error(`File not found: ${filePath}`);
6084
+ }
6085
+ const content = fs.readFileSync(filePath, "utf-8");
6086
+ const ext = path.extname(filePath).toLowerCase();
6087
+ const baseName = path.basename(filePath, ext);
6088
+ if (ext === ".graphql" || ext === ".gql") {
6089
+ return {
6090
+ sourceType: "graphql",
6091
+ data: content,
6092
+ derivedResourceName: options.resource || baseName,
6093
+ sourceDescription: options.file
6094
+ };
6095
+ }
6096
+ try {
6097
+ const json = JSON.parse(content);
6098
+ if (typeof json === "object" && json !== null && (("openapi" in json) || ("swagger" in json) || ("paths" in json) && ("components" in json))) {
6099
+ return {
6100
+ sourceType: "openapi",
6101
+ data: json,
6102
+ derivedResourceName: options.resource,
6103
+ sourceDescription: options.file
6104
+ };
6105
+ }
6106
+ if (typeof json === "object" && json !== null && (("__schema" in json) || ("data" in json) && json.data && ("__schema" in json.data) || ("types" in json) && Array.isArray(json.types))) {
6107
+ return {
6108
+ sourceType: "graphql",
6109
+ data: json,
6110
+ derivedResourceName: options.resource,
6111
+ sourceDescription: options.file
6112
+ };
6113
+ }
6114
+ const records = Array.isArray(json) ? json : Array.isArray(json.data) ? json.data : Array.isArray(json.items) ? json.items : [json];
6115
+ return {
6116
+ sourceType: "rest",
6117
+ data: records,
6118
+ derivedResourceName: options.resource || baseName,
6119
+ sourceDescription: options.file
6120
+ };
6121
+ } catch {
6122
+ if (content.includes("type ") || content.includes("enum ") || content.includes("schema ")) {
6123
+ return {
6124
+ sourceType: "graphql",
6125
+ data: content,
6126
+ derivedResourceName: options.resource || baseName,
6127
+ sourceDescription: options.file
6128
+ };
6129
+ }
6130
+ throw new Error(`Unable to parse file ${filePath}. Expected valid JSON or GraphQL SDL schema.`);
6131
+ }
6132
+ }
6133
+ if (options.url) {
6134
+ const url = options.url;
6135
+ const isGraphQL = options.type === "graphql" || url.endsWith("/graphql") || url.includes("/graphql?");
6136
+ if (isGraphQL) {
6137
+ const response2 = await customFetch(url, {
6138
+ method: "POST",
6139
+ headers: {
6140
+ "Content-Type": "application/json",
6141
+ Accept: "application/json",
6142
+ ...options.headers ?? {}
6143
+ },
6144
+ body: JSON.stringify({ query: GRAPHQL_INTROSPECTION_QUERY })
6145
+ });
6146
+ if (!response2.ok) {
6147
+ throw new Error(`GraphQL introspection request failed: HTTP ${response2.status} ${response2.statusText}`);
6148
+ }
6149
+ const json2 = await response2.json();
6150
+ return {
6151
+ sourceType: "graphql",
6152
+ data: json2,
6153
+ derivedResourceName: options.resource,
6154
+ sourceDescription: url
6155
+ };
6156
+ }
6157
+ const response = await customFetch(url, {
6158
+ method: options.method ?? "GET",
6159
+ headers: {
6160
+ Accept: "application/json",
6161
+ ...options.headers ?? {}
6162
+ },
6163
+ body: options.body
6164
+ });
6165
+ if (!response.ok) {
6166
+ throw new Error(`HTTP request failed: HTTP ${response.status} ${response.statusText}`);
6167
+ }
6168
+ const json = await response.json();
6169
+ if (typeof json === "object" && json !== null && (("openapi" in json) || ("swagger" in json) || ("paths" in json) && ("components" in json))) {
6170
+ return {
6171
+ sourceType: "openapi",
6172
+ data: json,
6173
+ derivedResourceName: options.resource,
6174
+ sourceDescription: url
6175
+ };
6176
+ }
6177
+ if (typeof json === "object" && json !== null && (("__schema" in json) || ("data" in json) && json.data?.__schema)) {
6178
+ return {
6179
+ sourceType: "graphql",
6180
+ data: json,
6181
+ derivedResourceName: options.resource,
6182
+ sourceDescription: url
6183
+ };
6184
+ }
6185
+ let derivedName = options.resource;
6186
+ if (!derivedName) {
6187
+ try {
6188
+ const parsedUrl = new URL(url);
6189
+ const segments = parsedUrl.pathname.split("/").filter(Boolean);
6190
+ derivedName = segments.pop() || "items";
6191
+ } catch {
6192
+ derivedName = "items";
6193
+ }
6194
+ }
6195
+ const records = Array.isArray(json) ? json : Array.isArray(json.data) ? json.data : Array.isArray(json.items) ? json.items : [json];
6196
+ return {
6197
+ sourceType: "rest",
6198
+ data: records,
6199
+ derivedResourceName: derivedName,
6200
+ sourceDescription: url
6201
+ };
6202
+ }
6203
+ throw new Error("Must provide either --url <url> or --file <path>");
6204
+ }
6205
+ function planGeneratedFiles(resources, bundles, format = "all") {
6206
+ const files = [];
6207
+ const exportLines = [];
6208
+ for (const resource of resources) {
6209
+ const bundle = bundles.get(resource.name);
6210
+ if (!bundle)
6211
+ continue;
6212
+ const baseName = resource.name;
6213
+ const pascalName = capitalize2(resource.name.endsWith("s") ? resource.name.slice(0, -1) : resource.name);
6214
+ if (format === "all" || format === "resource") {
6215
+ files.push({
6216
+ relativePath: `${baseName}.resource.ts`,
6217
+ content: bundle.code
6218
+ });
6219
+ exportLines.push(`export * from './${baseName}.resource.js';`);
6220
+ }
6221
+ if (format === "all" || format === "typebox") {
6222
+ files.push({
6223
+ relativePath: `${baseName}.schema.ts`,
6224
+ content: bundle.typeboxCode
6225
+ });
6226
+ exportLines.push(`export * from './${baseName}.schema.js';`);
6227
+ }
6228
+ if (format === "all" || format === "components") {
6229
+ files.push({
6230
+ relativePath: `${baseName}/ListPage.svelte`,
6231
+ content: bundle.componentCode.list
6232
+ });
6233
+ files.push({
6234
+ relativePath: `${baseName}/CreatePage.svelte`,
6235
+ content: bundle.componentCode.create
6236
+ });
6237
+ files.push({
6238
+ relativePath: `${baseName}/EditPage.svelte`,
6239
+ content: bundle.componentCode.edit
6240
+ });
6241
+ files.push({
6242
+ relativePath: `${baseName}/ShowPage.svelte`,
6243
+ content: bundle.componentCode.show
6244
+ });
6245
+ exportLines.push(`export { default as ${pascalName}ListPage } from './${baseName}/ListPage.svelte';`);
6246
+ exportLines.push(`export { default as ${pascalName}CreatePage } from './${baseName}/CreatePage.svelte';`);
6247
+ exportLines.push(`export { default as ${pascalName}EditPage } from './${baseName}/EditPage.svelte';`);
6248
+ exportLines.push(`export { default as ${pascalName}ShowPage } from './${baseName}/ShowPage.svelte';`);
6249
+ }
6250
+ }
6251
+ if (files.length > 0) {
6252
+ files.push({
6253
+ relativePath: "index.ts",
6254
+ content: `// Auto-generated by @svadmin/create infer
6255
+
6256
+ ${exportLines.join(`
6257
+ `)}
6258
+ `
6259
+ });
6260
+ }
6261
+ return files;
6262
+ }
6263
+ async function executeInfer(options, customFetch = fetch) {
6264
+ const loaded = await loadSourceData(options, customFetch);
6265
+ const primaryKey = options.primaryKey ?? "id";
6266
+ let resources;
6267
+ const bundles = new Map;
6268
+ if (loaded.sourceType === "openapi") {
6269
+ resources = inferFromOpenAPI(loaded.data, {
6270
+ primaryKey,
6271
+ include: options.resource ? [options.resource] : undefined
6272
+ });
6273
+ for (const res of resources) {
6274
+ bundles.set(res.name, generateResourceBundle(res));
6275
+ }
6276
+ } else if (loaded.sourceType === "graphql") {
6277
+ resources = inferFromGraphQL(loaded.data, {
6278
+ primaryKey,
6279
+ include: options.resource ? [options.resource] : undefined
6280
+ });
6281
+ for (const res of resources) {
6282
+ bundles.set(res.name, generateResourceBundle(res));
6283
+ }
6284
+ } else {
6285
+ const resName = options.resource || loaded.derivedResourceName || "items";
6286
+ const sampleArray = Array.isArray(loaded.data) ? loaded.data : [loaded.data];
6287
+ const inferRes = inferResource(resName, sampleArray, { primaryKey });
6288
+ resources = [inferRes.resource];
6289
+ bundles.set(resName, inferRes);
6290
+ }
6291
+ if (resources.length === 0) {
6292
+ throw new Error(`No resources could be inferred from ${loaded.sourceDescription}`);
6293
+ }
6294
+ const files = planGeneratedFiles(resources, bundles, options.format);
6295
+ let wrote = false;
6296
+ if (options.outDir && options.write) {
6297
+ const targetDir = path.resolve(process.cwd(), options.outDir);
6298
+ for (const file of files) {
6299
+ const fullPath = path.join(targetDir, file.relativePath);
6300
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
6301
+ fs.writeFileSync(fullPath, file.content, "utf-8");
6302
+ }
6303
+ wrote = true;
6304
+ }
6305
+ return {
6306
+ resources,
6307
+ bundles,
6308
+ files,
6309
+ sourceDescription: loaded.sourceDescription,
6310
+ wrote,
6311
+ outDir: options.outDir
6312
+ };
6313
+ }
6314
+ function printInferResult(result) {
6315
+ console.log();
6316
+ console.log(import_picocolors.default.cyan(" ╔═══════════════════════════════════╗"));
6317
+ console.log(import_picocolors.default.cyan(" ║ ") + import_picocolors.default.bold("svadmin infer") + import_picocolors.default.cyan(" ║"));
6318
+ console.log(import_picocolors.default.cyan(" ║ ") + import_picocolors.default.dim("Automated Code & UI Inferencer") + import_picocolors.default.cyan(" ║"));
6319
+ console.log(import_picocolors.default.cyan(" ╚═══════════════════════════════════╝"));
6320
+ console.log();
6321
+ console.log(import_picocolors.default.bold(` Source: ${import_picocolors.default.cyan(result.sourceDescription)}`));
6322
+ console.log(import_picocolors.default.bold(` Inferred: ${import_picocolors.default.green(result.resources.length.toString())} resource(s): ${result.resources.map((r) => r.name).join(", ")}`));
6323
+ console.log();
6324
+ if (result.outDir) {
6325
+ if (result.wrote) {
6326
+ console.log(import_picocolors.default.green(` ✔ Written ${result.files.length} file(s) to ${import_picocolors.default.cyan(result.outDir)}:`));
6327
+ for (const file of result.files) {
6328
+ console.log(` ${import_picocolors.default.green("•")} ${path.join(result.outDir, file.relativePath)}`);
6329
+ }
6330
+ console.log();
6331
+ console.log(import_picocolors.default.dim(" Next step: import and register your generated resources in src/resources.ts"));
6332
+ } else {
6333
+ console.log(import_picocolors.default.yellow(` Dry run plan — ${result.files.length} file(s) planned for ${import_picocolors.default.cyan(result.outDir)}:`));
6334
+ for (const file of result.files) {
6335
+ console.log(` ${import_picocolors.default.cyan("•")} ${path.join(result.outDir, file.relativePath)}`);
6336
+ }
6337
+ console.log();
6338
+ console.log(import_picocolors.default.yellow(" Dry run only; re-run with --write to generate files to disk."));
6339
+ }
6340
+ } else {
6341
+ for (const res of result.resources) {
6342
+ const bundle = result.bundles.get(res.name);
6343
+ console.log(import_picocolors.default.bold(` Resource: ${import_picocolors.default.green(res.name)} (${res.label})`));
6344
+ console.log(` Primary Key: ${import_picocolors.default.cyan(res.primaryKey ?? "id")} | Fields: ${res.fields.length}`);
6345
+ console.log(import_picocolors.default.dim(" Fields:"));
6346
+ for (const f of res.fields) {
6347
+ const badges = [
6348
+ f.type,
6349
+ f.required ? "required" : null,
6350
+ f.resource ? `-> ${f.resource}` : null,
6351
+ f.showInList ? "list" : null,
6352
+ f.showInForm ? "form" : null
6353
+ ].filter(Boolean).join(", ");
6354
+ console.log(` ${import_picocolors.default.cyan(f.key)}: ${import_picocolors.default.dim(`(${badges})`)}`);
6355
+ }
6356
+ console.log();
6357
+ if (bundle) {
6358
+ console.log(import_picocolors.default.bold(" TypeBox Schema:"));
6359
+ console.log(import_picocolors.default.dim(" ───────────────────────────────────"));
6360
+ console.log(bundle.typeboxCode.trim());
6361
+ console.log(import_picocolors.default.dim(" ───────────────────────────────────"));
6362
+ console.log();
6363
+ }
6364
+ }
6365
+ console.log(import_picocolors.default.dim(" Tip: Pass --out-dir src/resources --write to save generated files directly."));
6366
+ }
6367
+ console.log();
6368
+ }
6369
+ async function promptInferWizard() {
6370
+ const answers = await import_prompts.default([
6371
+ {
6372
+ type: "select",
6373
+ name: "sourceType",
6374
+ message: "Select API source type:",
6375
+ choices: [
6376
+ { title: "REST API Endpoint (URL)", value: "rest-url", description: "Fetch sample JSON data from REST endpoint" },
6377
+ { title: "OpenAPI / Swagger Spec (URL or File)", value: "openapi", description: "OpenAPI 3.x / Swagger schema definition" },
6378
+ { title: "GraphQL Endpoint (URL or Schema File)", value: "graphql", description: "GraphQL introspection or SDL schema" },
6379
+ { title: "Local Sample JSON File", value: "rest-file", description: "JSON file with sample data records" }
6380
+ ],
6381
+ initial: 0
6382
+ },
6383
+ {
6384
+ type: (prev) => prev === "rest-file" || prev === "openapi" ? "text" : null,
6385
+ name: "pathOrUrl",
6386
+ message: "Enter URL or File path:",
6387
+ validate: (v) => v.trim() ? true : "URL or file path is required"
6388
+ },
6389
+ {
6390
+ type: (prev, values) => values.sourceType === "rest-url" || values.sourceType === "graphql" ? "text" : null,
6391
+ name: "endpointUrl",
6392
+ message: "Enter Endpoint URL:",
6393
+ validate: (v) => v.trim().startsWith("http") ? true : "Must be a valid HTTP(S) URL"
6394
+ },
6395
+ {
6396
+ type: "text",
6397
+ name: "outDir",
6398
+ message: "Target output directory:",
6399
+ initial: "src/resources"
6400
+ },
6401
+ {
6402
+ type: "confirm",
6403
+ name: "write",
6404
+ message: "Write generated files to disk now?",
6405
+ initial: true
6406
+ }
6407
+ ]);
6408
+ const targetPath = answers.pathOrUrl || answers.endpointUrl;
6409
+ const isUrl = targetPath?.startsWith("http://") || targetPath?.startsWith("https://");
6410
+ return {
6411
+ url: isUrl ? targetPath : undefined,
6412
+ file: !isUrl ? targetPath : undefined,
6413
+ type: answers.sourceType.startsWith("graphql") ? "graphql" : answers.sourceType.startsWith("openapi") ? "openapi" : "rest",
6414
+ outDir: answers.outDir,
6415
+ write: answers.write
6416
+ };
6417
+ }
6418
+ async function inferCommand(args) {
6419
+ let options = parseInferArguments(args);
6420
+ if (!options.url && !options.file && process.stdin.isTTY) {
6421
+ options = await promptInferWizard();
6422
+ }
6423
+ if (!options.url && !options.file) {
6424
+ printInferHelp();
6425
+ return;
6426
+ }
6427
+ const result = await executeInfer(options);
6428
+ printInferResult(result);
6429
+ }
6430
+ function capitalize2(s) {
6431
+ return s.charAt(0).toUpperCase() + s.slice(1);
6432
+ }
6433
+
6434
+ // src/lite-init.ts
6435
+ import fs2 from "node:fs";
6436
+ import path2 from "node:path";
6437
+ var GENERATED_FILES = {
6438
+ "src/lib/svadmin-lite.ts": `import { dataProvider, resources } from '$lib/admin';
6439
+ import type { ResourceDefinition } from '@svadmin/core';
6440
+
6441
+ export { dataProvider, resources };
6442
+
6443
+ export function getResource(name: string): ResourceDefinition | undefined {
6444
+ return resources.find((resource) => resource.name === name);
6445
+ }
6446
+ `,
6447
+ "src/routes/lite/+layout.ts": `export const ssr = true;
6448
+ export const csr = false;
6449
+ `,
6450
+ "src/routes/lite/+layout.server.ts": `import { resources } from '$lib/svadmin-lite';
6451
+ import type { LayoutServerLoad } from './$types';
6452
+
6453
+ export const load = (({ url }) => {
6454
+ const segments = url.pathname.split('/').filter(Boolean);
6455
+ const currentResource = segments[1] ?? '';
6456
+
6457
+ return { resources, currentResource };
6458
+ }) satisfies LayoutServerLoad;
6459
+ `,
6460
+ "src/routes/lite/+layout.svelte": `<script lang="ts">
6461
+ import type { Snippet } from 'svelte';
6462
+ import { LiteLayout } from '@svadmin/lite';
6463
+ import '@svadmin/lite/lite.css';
6464
+ import type { LayoutData } from './$types';
6465
+
6466
+ let { data, children }: { data: LayoutData; children: Snippet } = $props();
6467
+ </script>
6468
+
6469
+ <LiteLayout
6470
+ resources={data.resources}
6471
+ currentResource={data.currentResource}
6472
+ brandName="Lite Admin"
6473
+ basePath="/lite"
6474
+ >
6475
+ {@render children()}
6476
+ </LiteLayout>
6477
+ `,
6478
+ "src/routes/lite/+page.server.ts": `import { error, redirect } from '@sveltejs/kit';
6479
+ import { resources } from '$lib/svadmin-lite';
6480
+ import type { PageServerLoad } from './$types';
6481
+
6482
+ export const load = (() => {
6483
+ const firstResource = resources[0];
6484
+ if (!firstResource) throw error(404, 'No Lite resources configured');
6485
+ throw redirect(302, \`/lite/\${firstResource.name}\`);
6486
+ }) satisfies PageServerLoad;
6487
+ `,
6488
+ "src/routes/lite/[resource]/+page.server.ts": `import { error } from '@sveltejs/kit';
6489
+ import { createCrudActions, createListLoader } from '@svadmin/lite';
6490
+ import { dataProvider, getResource } from '$lib/svadmin-lite';
6491
+ import type { Actions, PageServerLoad } from './$types';
6492
+
6493
+ export const load = ((event) => {
6494
+ const resource = getResource(event.params.resource);
6495
+ if (!resource) throw error(404, \`Resource "\${event.params.resource}" not found\`);
6496
+ return createListLoader(dataProvider, resource)(event);
6497
+ }) satisfies PageServerLoad;
6498
+
6499
+ export const actions = {
6500
+ delete: (event) => {
6501
+ const resource = getResource(event.params.resource);
6502
+ if (!resource) throw error(404, \`Resource "\${event.params.resource}" not found\`);
6503
+ return createCrudActions(dataProvider, resource).delete(event);
6504
+ },
6505
+ batchDelete: (event) => {
6506
+ const resource = getResource(event.params.resource);
6507
+ if (!resource) throw error(404, \`Resource "\${event.params.resource}" not found\`);
6508
+ return createCrudActions(dataProvider, resource).batchDelete(event);
6509
+ },
6510
+ } satisfies Actions;
6511
+ `,
6512
+ "src/routes/lite/[resource]/+page.svelte": `<script lang="ts">
6513
+ import { LiteListPage } from '@svadmin/lite';
6514
+ import type { PageProps } from './$types';
6515
+
6516
+ let { data }: PageProps = $props();
6517
+ </script>
6518
+
6519
+ <LiteListPage {...data} basePath="/lite" />
6520
+ `,
6521
+ "src/routes/lite/[resource]/create/+page.server.ts": `import { error, redirect } from '@sveltejs/kit';
6522
+ import { createCrudActions } from '@svadmin/lite';
6523
+ import { dataProvider, getResource } from '$lib/svadmin-lite';
6524
+ import type { Actions, PageServerLoad } from './$types';
6525
+
6526
+ export const load = (({ params }) => {
6527
+ const resource = getResource(params.resource);
6528
+ if (!resource) throw error(404, \`Resource "\${params.resource}" not found\`);
6529
+ return { resource };
6530
+ }) satisfies PageServerLoad;
6531
+
6532
+ export const actions = {
6533
+ create: async (event) => {
6534
+ const resource = getResource(event.params.resource);
6535
+ if (!resource) throw error(404, \`Resource "\${event.params.resource}" not found\`);
6536
+ const result = await createCrudActions(dataProvider, resource).create(event);
6537
+ if (result && 'success' in result && result.success) {
6538
+ throw redirect(303, \`/lite/\${resource.name}\`);
6539
+ }
6540
+ return result;
6541
+ },
6542
+ } satisfies Actions;
6543
+ `,
6544
+ "src/routes/lite/[resource]/create/+page.svelte": `<script lang="ts">
6545
+ import { LiteCreatePage } from '@svadmin/lite';
6546
+ import type { PageProps } from './$types';
6547
+
6548
+ let { data, form }: PageProps = $props();
6549
+ </script>
6550
+
6551
+ <LiteCreatePage
6552
+ resource={data.resource}
6553
+ errors={form?.errors}
6554
+ values={form?.values}
6555
+ basePath="/lite"
6556
+ />
6557
+ `,
6558
+ "src/routes/lite/[resource]/show/[id]/+page.server.ts": `import { error } from '@sveltejs/kit';
6559
+ import { createDetailLoader } from '@svadmin/lite';
6560
+ import { dataProvider, getResource } from '$lib/svadmin-lite';
6561
+ import type { PageServerLoad } from './$types';
6562
+
6563
+ export const load = ((event) => {
6564
+ const resource = getResource(event.params.resource);
6565
+ if (!resource) throw error(404, \`Resource "\${event.params.resource}" not found\`);
6566
+ return createDetailLoader(dataProvider, resource)(event);
6567
+ }) satisfies PageServerLoad;
6568
+ `,
6569
+ "src/routes/lite/[resource]/show/[id]/+page.svelte": `<script lang="ts">
6570
+ import { LiteShowPage } from '@svadmin/lite';
6571
+ import type { PageProps } from './$types';
6572
+
6573
+ let { data }: PageProps = $props();
6574
+ </script>
6575
+
6576
+ <LiteShowPage resource={data.resource} record={data.record} basePath="/lite" />
6577
+ `,
6578
+ "src/routes/lite/[resource]/edit/[id]/+page.server.ts": `import { error, redirect } from '@sveltejs/kit';
6579
+ import { createCrudActions, createDetailLoader } from '@svadmin/lite';
6580
+ import { dataProvider, getResource } from '$lib/svadmin-lite';
6581
+ import type { Actions, PageServerLoad } from './$types';
6582
+
6583
+ export const load = ((event) => {
6584
+ const resource = getResource(event.params.resource);
6585
+ if (!resource) throw error(404, \`Resource "\${event.params.resource}" not found\`);
6586
+ return createDetailLoader(dataProvider, resource)(event);
6587
+ }) satisfies PageServerLoad;
6588
+
6589
+ export const actions = {
6590
+ update: async (event) => {
6591
+ const resource = getResource(event.params.resource);
6592
+ if (!resource) throw error(404, \`Resource "\${event.params.resource}" not found\`);
6593
+ const result = await createCrudActions(dataProvider, resource).update(event);
6594
+ if (result && 'success' in result && result.success) {
6595
+ throw redirect(303, \`/lite/\${resource.name}/show/\${event.params.id}\`);
6596
+ }
6597
+ return result;
6598
+ },
6599
+ delete: (event) => {
6600
+ const resource = getResource(event.params.resource);
6601
+ if (!resource) throw error(404, \`Resource "\${event.params.resource}" not found\`);
6602
+ return createCrudActions(dataProvider, resource).delete(event);
6603
+ },
6604
+ } satisfies Actions;
6605
+ `,
6606
+ "src/routes/lite/[resource]/edit/[id]/+page.svelte": `<script lang="ts">
6607
+ import { LiteEditPage } from '@svadmin/lite';
6608
+ import type { PageProps } from './$types';
6609
+
6610
+ let { data, form }: PageProps = $props();
6611
+ </script>
6612
+
6613
+ <LiteEditPage
6614
+ resource={data.resource}
6615
+ record={data.record}
6616
+ errors={form?.errors}
6617
+ basePath="/lite"
6618
+ />
6619
+ `
6620
+ };
6621
+ function parseLiteInitArguments(args) {
6622
+ let write = false;
6623
+ const positional = [];
6624
+ for (const argument of args) {
6625
+ if (argument === "--write")
6626
+ write = true;
6627
+ else if (argument.startsWith("-"))
6628
+ throw new Error(`Unknown option: ${argument}`);
6629
+ else
6630
+ positional.push(argument);
6631
+ }
6632
+ if (positional.length > 1) {
6633
+ throw new Error(`Expected at most one project directory, received: ${positional.join(", ")}`);
6634
+ }
6635
+ return {
6636
+ projectDirectory: path2.resolve(process.cwd(), positional[0] ?? "."),
6637
+ write
6638
+ };
6639
+ }
6640
+ function assertLiteProject(projectDirectory) {
6641
+ if (!fs2.existsSync(projectDirectory)) {
6642
+ throw new Error(`Project directory does not exist: ${projectDirectory}`);
6643
+ }
6644
+ if (!fs2.existsSync(path2.join(projectDirectory, "package.json"))) {
6645
+ throw new Error(`Not a Node project: ${path2.join(projectDirectory, "package.json")} is missing`);
6646
+ }
6647
+ if (!fs2.existsSync(path2.join(projectDirectory, "src", "routes"))) {
6648
+ throw new Error("Lite routes require a SvelteKit project with src/routes. Keep the existing SPA and add a SvelteKit Lite app alongside it.");
6649
+ }
6650
+ const adminModuleExists = ["ts", "js", "svelte"].some((extension) => fs2.existsSync(path2.join(projectDirectory, "src", "lib", `admin.${extension}`)));
6651
+ if (!adminModuleExists) {
6652
+ throw new Error("Lite routes require src/lib/admin.ts (or .js/.svelte) exporting resources and dataProvider.");
6653
+ }
6654
+ }
6655
+ function planLiteInit(projectDirectory) {
6656
+ assertLiteProject(projectDirectory);
6657
+ const entries = Object.entries(GENERATED_FILES).map(([relativePath, content]) => {
6658
+ const filePath = path2.join(projectDirectory, relativePath);
6659
+ return { filePath, relativePath, content, exists: fs2.existsSync(filePath) };
6660
+ });
6661
+ return { projectDirectory, entries };
6662
+ }
6663
+ function writeLiteInit(plan) {
6664
+ const written = [];
6665
+ const preserved = [];
6666
+ for (const entry of plan.entries) {
6667
+ if (entry.exists || fs2.existsSync(entry.filePath)) {
6668
+ preserved.push(entry.relativePath);
6669
+ continue;
6670
+ }
6671
+ fs2.mkdirSync(path2.dirname(entry.filePath), { recursive: true });
6672
+ fs2.writeFileSync(entry.filePath, entry.content);
6673
+ written.push(entry.relativePath);
6674
+ }
6675
+ return { plan, written, preserved };
6676
+ }
6677
+ function liteInitCommand(args) {
6678
+ const options = parseLiteInitArguments(args);
6679
+ const plan = planLiteInit(options.projectDirectory);
6680
+ console.log(`
6681
+ svadmin lite init — ${options.projectDirectory}`);
6682
+ for (const entry of plan.entries) {
6683
+ console.log(` ${entry.exists ? "preserve" : "add"} ${entry.relativePath}`);
6684
+ }
6685
+ if (!options.write) {
6686
+ console.log(`
6687
+ Dry run only; re-run with --write to add missing Lite routes.`);
6688
+ return;
6689
+ }
6690
+ const result = writeLiteInit(plan);
6691
+ console.log(`
6692
+ Written ${result.written.length} file(s); preserved ${result.preserved.length} existing file(s).`);
6693
+ }
6694
+
5130
6695
  // src/project-maintenance.ts
5131
6696
  import {
5132
6697
  constants,
@@ -5136,10 +6701,10 @@ import {
5136
6701
  unlinkSync,
5137
6702
  writeFileSync
5138
6703
  } from "node:fs";
5139
- function parseDependencyMap(candidate, path) {
6704
+ function parseDependencyMap(candidate, path3) {
5140
6705
  if (candidate === undefined)
5141
6706
  return;
5142
- assertStringRecord(candidate, path);
6707
+ assertStringRecord(candidate, path3);
5143
6708
  return { ...candidate };
5144
6709
  }
5145
6710
  function parseMaintainedPackageJson(packageJsonCandidate) {
@@ -5415,15 +6980,15 @@ function writeProjectPackageJsonUpgrade(packagePath, scaffold, backupDate) {
5415
6980
 
5416
6981
  // src/index.ts
5417
6982
  var __filename2 = fileURLToPath(import.meta.url);
5418
- var __dirname2 = path.dirname(__filename2);
6983
+ var __dirname2 = path3.dirname(__filename2);
5419
6984
  function loadShippedScaffoldManifest() {
5420
- return loadScaffoldManifest(path.join(__dirname2, "..", "scaffold-manifest.json"));
6985
+ return loadScaffoldManifest(path3.join(__dirname2, "..", "scaffold-manifest.json"));
5421
6986
  }
5422
6987
  function projectDirectoryFromArguments(positional) {
5423
6988
  if (positional.length > 1) {
5424
6989
  throw new Error(`Expected at most one project directory, received: ${positional.join(", ")}`);
5425
6990
  }
5426
- return path.resolve(process.cwd(), positional[0] ?? ".");
6991
+ return path3.resolve(process.cwd(), positional[0] ?? ".");
5427
6992
  }
5428
6993
  function doctorProjectDirectory(args) {
5429
6994
  const unknownOption = args.find((argument) => argument.startsWith("-"));
@@ -5459,52 +7024,52 @@ function upgradeChangeMessage(change) {
5459
7024
  return `update ${change.packageName} from ${change.from ?? "missing"} to ${change.to}`;
5460
7025
  }
5461
7026
  function printDoctorIssue(issue) {
5462
- const marker = issue.kind === "drift" || issue.kind === "section" ? import_picocolors.default.yellow(" ⚠") : import_picocolors.default.red(" ✗");
7027
+ const marker = issue.kind === "drift" || issue.kind === "section" ? import_picocolors2.default.yellow(" ⚠") : import_picocolors2.default.red(" ✗");
5463
7028
  console.log(`${marker} ${doctorIssueMessage(issue)}`);
5464
- console.log(import_picocolors.default.dim(` → ${issue.action}`));
7029
+ console.log(import_picocolors2.default.dim(` → ${issue.action}`));
5465
7030
  }
5466
7031
  function printDoctorReport(report, projectDirectory) {
5467
7032
  console.log();
5468
- console.log(import_picocolors.default.bold(`svadmin doctor — ${projectDirectory}`));
7033
+ console.log(import_picocolors2.default.bold(`svadmin doctor — ${projectDirectory}`));
5469
7034
  if (report.status === "clean") {
5470
- console.log(import_picocolors.default.green(" ✔ Dependencies match the shipped svadmin scaffold."));
7035
+ console.log(import_picocolors2.default.green(" ✔ Dependencies match the shipped svadmin scaffold."));
5471
7036
  } else {
5472
7037
  for (const issue of report.issues)
5473
7038
  printDoctorIssue(issue);
5474
7039
  console.log();
5475
- console.log(import_picocolors.default.yellow(` ${report.issues.length} actionable issue(s) found.`));
7040
+ console.log(import_picocolors2.default.yellow(` ${report.issues.length} actionable issue(s) found.`));
5476
7041
  }
5477
7042
  console.log();
5478
7043
  }
5479
7044
  function doctor(args) {
5480
7045
  const projectDirectory = doctorProjectDirectory(args);
5481
- const project = readMaintainedPackageJson(path.join(projectDirectory, "package.json"));
7046
+ const project = readMaintainedPackageJson(path3.join(projectDirectory, "package.json"));
5482
7047
  const report = doctorProjectPackageJson(project, loadShippedScaffoldManifest());
5483
7048
  printDoctorReport(report, projectDirectory);
5484
7049
  process.exitCode = report.exitCode;
5485
7050
  }
5486
7051
  function printUpgradeChanges(upgradeExecution) {
5487
7052
  for (const change of upgradeExecution.plan.changes) {
5488
- console.log(` ${import_picocolors.default.cyan("•")} ${upgradeChangeMessage(change)}`);
7053
+ console.log(` ${import_picocolors2.default.cyan("•")} ${upgradeChangeMessage(change)}`);
5489
7054
  }
5490
7055
  console.log();
5491
7056
  }
5492
7057
  function printUpgradeOutcome(upgradeExecution, packagePath) {
5493
7058
  if (upgradeExecution.wrote) {
5494
- console.log(import_picocolors.default.green(" ✔ package.json updated."));
5495
- console.log(` Backup: ${import_picocolors.default.cyan(upgradeExecution.backupPath)}`);
5496
- console.log(` Restore by copying the backup over: ${import_picocolors.default.cyan(packagePath)}`);
7059
+ console.log(import_picocolors2.default.green(" ✔ package.json updated."));
7060
+ console.log(` Backup: ${import_picocolors2.default.cyan(upgradeExecution.backupPath)}`);
7061
+ console.log(` Restore by copying the backup over: ${import_picocolors2.default.cyan(packagePath)}`);
5497
7062
  } else {
5498
- console.log(import_picocolors.default.yellow(" Dry run only; package.json was not changed."));
7063
+ console.log(import_picocolors2.default.yellow(" Dry run only; package.json was not changed."));
5499
7064
  console.log(" Re-run this command with --write to apply the plan.");
5500
7065
  }
5501
7066
  console.log();
5502
7067
  }
5503
7068
  function printUpgradeExecution(upgradeExecution, projectDirectory, packagePath) {
5504
7069
  console.log();
5505
- console.log(import_picocolors.default.bold(`svadmin upgrade — ${projectDirectory}`));
7070
+ console.log(import_picocolors2.default.bold(`svadmin upgrade — ${projectDirectory}`));
5506
7071
  if (upgradeExecution.plan.changes.length === 0) {
5507
- console.log(import_picocolors.default.green(" ✔ package.json already matches the shipped scaffold."));
7072
+ console.log(import_picocolors2.default.green(" ✔ package.json already matches the shipped scaffold."));
5508
7073
  console.log();
5509
7074
  return;
5510
7075
  }
@@ -5513,60 +7078,60 @@ function printUpgradeExecution(upgradeExecution, projectDirectory, packagePath)
5513
7078
  }
5514
7079
  function upgrade(args) {
5515
7080
  const commandArguments = parseUpgradeArguments(args);
5516
- const packagePath = path.join(commandArguments.projectDirectory, "package.json");
7081
+ const packagePath = path3.join(commandArguments.projectDirectory, "package.json");
5517
7082
  const scaffoldManifest = loadShippedScaffoldManifest();
5518
7083
  const upgradeExecution = commandArguments.write ? writeProjectPackageJsonUpgrade(packagePath, scaffoldManifest, new Date) : planProjectPackageFileUpgrade(packagePath, scaffoldManifest);
5519
7084
  printUpgradeExecution(upgradeExecution, commandArguments.projectDirectory, packagePath);
5520
7085
  }
5521
7086
  var GUIDANCE_FILES = ["DESIGN.md", "AGENTS.md"];
5522
7087
  function missingGuidanceFiles(projectDirectory) {
5523
- return GUIDANCE_FILES.filter((fileName) => !fs.existsSync(path.join(projectDirectory, fileName)));
7088
+ return GUIDANCE_FILES.filter((fileName) => !fs3.existsSync(path3.join(projectDirectory, fileName)));
5524
7089
  }
5525
7090
  function printGuidancePlan(projectDirectory, missingFiles) {
5526
7091
  console.log();
5527
- console.log(import_picocolors.default.bold(`svadmin guidance — ${projectDirectory}`));
7092
+ console.log(import_picocolors2.default.bold(`svadmin guidance — ${projectDirectory}`));
5528
7093
  for (const fileName of missingFiles) {
5529
- console.log(` ${import_picocolors.default.cyan("•")} add ${fileName}`);
7094
+ console.log(` ${import_picocolors2.default.cyan("•")} add ${fileName}`);
5530
7095
  }
5531
7096
  console.log();
5532
7097
  }
5533
7098
  function installMissingGuidanceFiles(guidanceDirectory, projectDirectory, missingFiles) {
5534
7099
  for (const fileName of missingFiles) {
5535
- fs.copyFileSync(path.join(guidanceDirectory, fileName), path.join(projectDirectory, fileName));
7100
+ fs3.copyFileSync(path3.join(guidanceDirectory, fileName), path3.join(projectDirectory, fileName));
5536
7101
  }
5537
7102
  }
5538
7103
  function guidance(args) {
5539
7104
  const { projectDirectory, write } = parseUpgradeArguments(args);
5540
- const guidanceDirectory = path.join(__dirname2, "..", "guidance");
5541
- if (!fs.existsSync(projectDirectory))
7105
+ const guidanceDirectory = path3.join(__dirname2, "..", "guidance");
7106
+ if (!fs3.existsSync(projectDirectory))
5542
7107
  throw new Error(`Project directory does not exist: ${projectDirectory}`);
5543
- if (!fs.existsSync(guidanceDirectory))
7108
+ if (!fs3.existsSync(guidanceDirectory))
5544
7109
  throw new Error("Shipped svadmin guidance files are missing");
5545
7110
  const missingFiles = missingGuidanceFiles(projectDirectory);
5546
7111
  if (missingFiles.length === 0) {
5547
- console.log(import_picocolors.default.green(`
7112
+ console.log(import_picocolors2.default.green(`
5548
7113
  ✔ DESIGN.md and AGENTS.md already exist; nothing was changed.
5549
7114
  `));
5550
7115
  return;
5551
7116
  }
5552
7117
  printGuidancePlan(projectDirectory, missingFiles);
5553
7118
  if (!write) {
5554
- console.log(import_picocolors.default.yellow(` Dry run only; re-run with --write to add missing guidance files.
7119
+ console.log(import_picocolors2.default.yellow(` Dry run only; re-run with --write to add missing guidance files.
5555
7120
  `));
5556
7121
  return;
5557
7122
  }
5558
7123
  installMissingGuidanceFiles(guidanceDirectory, projectDirectory, missingFiles);
5559
- console.log(import_picocolors.default.green(` ✔ Added ${missingFiles.length} guidance file(s); existing files were preserved.`));
7124
+ console.log(import_picocolors2.default.green(` ✔ Added ${missingFiles.length} guidance file(s); existing files were preserved.`));
5560
7125
  console.log();
5561
7126
  }
5562
7127
  async function init() {
5563
7128
  console.log();
5564
- console.log(import_picocolors.default.cyan(" ╔═══════════════════════════════════╗"));
5565
- console.log(import_picocolors.default.cyan(" ║ ") + import_picocolors.default.bold("create-svadmin") + import_picocolors.default.cyan(" ║"));
5566
- console.log(import_picocolors.default.cyan(" ║ ") + import_picocolors.default.dim("Headless Admin for Svelte 5") + import_picocolors.default.cyan(" ║"));
5567
- console.log(import_picocolors.default.cyan(" ╚═══════════════════════════════════╝"));
7129
+ console.log(import_picocolors2.default.cyan(" ╔═══════════════════════════════════╗"));
7130
+ console.log(import_picocolors2.default.cyan(" ║ ") + import_picocolors2.default.bold("create-svadmin") + import_picocolors2.default.cyan(" ║"));
7131
+ console.log(import_picocolors2.default.cyan(" ║ ") + import_picocolors2.default.dim("Headless Admin for Svelte 5") + import_picocolors2.default.cyan(" ║"));
7132
+ console.log(import_picocolors2.default.cyan(" ╚═══════════════════════════════════╝"));
5568
7133
  console.log();
5569
- const response = await import_prompts.default([
7134
+ const response = await import_prompts2.default([
5570
7135
  {
5571
7136
  type: "text",
5572
7137
  name: "projectName",
@@ -5575,7 +7140,7 @@ async function init() {
5575
7140
  validate: (value) => {
5576
7141
  if (!value.trim())
5577
7142
  return "Project name is required";
5578
- if (fs.existsSync(value.trim()) && fs.readdirSync(value.trim()).length > 0) {
7143
+ if (fs3.existsSync(value.trim()) && fs3.readdirSync(value.trim()).length > 0) {
5579
7144
  return "Directory already exists and is not empty";
5580
7145
  }
5581
7146
  return true;
@@ -5613,61 +7178,61 @@ async function init() {
5613
7178
  }
5614
7179
  ]);
5615
7180
  if (!response.projectName) {
5616
- console.log(import_picocolors.default.red(`
7181
+ console.log(import_picocolors2.default.red(`
5617
7182
  Operation cancelled.
5618
7183
  `));
5619
7184
  return;
5620
7185
  }
5621
- const projectDir = path.resolve(process.cwd(), response.projectName.trim());
5622
- if (!fs.existsSync(projectDir)) {
5623
- fs.mkdirSync(projectDir, { recursive: true });
7186
+ const projectDir = path3.resolve(process.cwd(), response.projectName.trim());
7187
+ if (!fs3.existsSync(projectDir)) {
7188
+ fs3.mkdirSync(projectDir, { recursive: true });
5624
7189
  }
5625
7190
  console.log(`
5626
- ${import_picocolors.default.bold("Scaffolding")} project in ${import_picocolors.default.green(projectDir)}...
7191
+ ${import_picocolors2.default.bold("Scaffolding")} project in ${import_picocolors2.default.green(projectDir)}...
5627
7192
  `);
5628
- const templateDir = path.join(__dirname2, "..", "template");
5629
- const guidanceDir = path.join(__dirname2, "..", "guidance");
7193
+ const templateDir = path3.join(__dirname2, "..", "template");
7194
+ const guidanceDir = path3.join(__dirname2, "..", "guidance");
5630
7195
  const scaffoldManifest = loadShippedScaffoldManifest();
5631
7196
  function copyDir(src, dest) {
5632
- fs.mkdirSync(dest, { recursive: true });
5633
- const entries = fs.readdirSync(src, { withFileTypes: true });
7197
+ fs3.mkdirSync(dest, { recursive: true });
7198
+ const entries = fs3.readdirSync(src, { withFileTypes: true });
5634
7199
  for (const entry of entries) {
5635
- const srcPath = path.join(src, entry.name);
5636
- const destPath = path.join(dest, entry.name === "_gitignore" ? ".gitignore" : entry.name);
7200
+ const srcPath = path3.join(src, entry.name);
7201
+ const destPath = path3.join(dest, entry.name === "_gitignore" ? ".gitignore" : entry.name);
5637
7202
  if (entry.isDirectory()) {
5638
7203
  copyDir(srcPath, destPath);
5639
7204
  } else {
5640
- fs.copyFileSync(srcPath, destPath);
7205
+ fs3.copyFileSync(srcPath, destPath);
5641
7206
  }
5642
7207
  }
5643
7208
  }
5644
- if (fs.existsSync(templateDir)) {
7209
+ if (fs3.existsSync(templateDir)) {
5645
7210
  copyDir(templateDir, projectDir);
5646
- console.log(import_picocolors.default.green(" ✔") + " Template files copied");
7211
+ console.log(import_picocolors2.default.green(" ✔") + " Template files copied");
5647
7212
  }
5648
- if (fs.existsSync(guidanceDir)) {
7213
+ if (fs3.existsSync(guidanceDir)) {
5649
7214
  copyDir(guidanceDir, projectDir);
5650
- console.log(import_picocolors.default.green(" ✔") + " AI and design guidance copied");
7215
+ console.log(import_picocolors2.default.green(" ✔") + " AI and design guidance copied");
5651
7216
  }
5652
7217
  const packageJson = createProjectPackageJson(scaffoldManifest, {
5653
7218
  projectName: response.projectName,
5654
7219
  dataProvider: response.dataProvider,
5655
7220
  authProvider: response.authProvider
5656
7221
  });
5657
- fs.writeFileSync(path.join(projectDir, "package.json"), `${JSON.stringify(packageJson, null, 2)}
7222
+ fs3.writeFileSync(path3.join(projectDir, "package.json"), `${JSON.stringify(packageJson, null, 2)}
5658
7223
  `);
5659
- console.log(import_picocolors.default.green(" ✔") + " package.json generated");
5660
- fs.writeFileSync(path.join(projectDir, ".gitignore"), `node_modules
7224
+ console.log(import_picocolors2.default.green(" ✔") + " package.json generated");
7225
+ fs3.writeFileSync(path3.join(projectDir, ".gitignore"), `node_modules
5661
7226
  dist
5662
7227
  .svelte-kit
5663
7228
  .env
5664
7229
  .env.local
5665
7230
  *.local
5666
7231
  `);
5667
- console.log(import_picocolors.default.green(" ✔") + " .gitignore generated");
7232
+ console.log(import_picocolors2.default.green(" ✔") + " .gitignore generated");
5668
7233
  const dpLabel = response.dataProvider === "simple-rest" ? "Simple REST" : response.dataProvider === "supabase" ? "Supabase" : response.dataProvider === "graphql" ? "GraphQL" : "Custom";
5669
7234
  const authLabel = response.authProvider === "mock" ? "Mock (demo)" : response.authProvider === "jwt" ? "JWT" : response.authProvider === "supabase" ? "Supabase Auth" : "None";
5670
- fs.writeFileSync(path.join(projectDir, "README.md"), `# ${response.projectName}
7235
+ fs3.writeFileSync(path3.join(projectDir, "README.md"), `# ${response.projectName}
5671
7236
 
5672
7237
  Built with [svadmin](https://github.com/vibeunion/svadmin) — Headless Admin Framework for Svelte 5.
5673
7238
 
@@ -5685,30 +7250,30 @@ bun run dev
5685
7250
  - **Auth**: ${authLabel}
5686
7251
  - **State**: TanStack Query v6
5687
7252
  `);
5688
- console.log(import_picocolors.default.green(" ✔") + " README.md generated");
7253
+ console.log(import_picocolors2.default.green(" ✔") + " README.md generated");
5689
7254
  if (response.installDeps) {
5690
7255
  console.log(`
5691
- ${import_picocolors.default.bold("Installing dependencies...")}
7256
+ ${import_picocolors2.default.bold("Installing dependencies...")}
5692
7257
  `);
5693
7258
  const bunInstall = spawnSync("bun", ["install"], { cwd: projectDir, stdio: "inherit" });
5694
7259
  if (bunInstall.status !== 0) {
5695
7260
  const npmInstall = spawnSync("npm", ["install"], { cwd: projectDir, stdio: "inherit" });
5696
7261
  if (npmInstall.status !== 0) {
5697
- console.log(import_picocolors.default.yellow("\n ⚠ Auto-install failed. Run `bun install` or `npm install` manually."));
7262
+ console.log(import_picocolors2.default.yellow("\n ⚠ Auto-install failed. Run `bun install` or `npm install` manually."));
5698
7263
  }
5699
7264
  }
5700
7265
  }
5701
7266
  console.log();
5702
- console.log(import_picocolors.default.green(import_picocolors.default.bold(" ✔ Project ready!")));
7267
+ console.log(import_picocolors2.default.green(import_picocolors2.default.bold(" ✔ Project ready!")));
5703
7268
  console.log();
5704
7269
  console.log(" Next steps:");
5705
- console.log(` ${import_picocolors.default.cyan(`cd ${response.projectName}`)}`);
7270
+ console.log(` ${import_picocolors2.default.cyan(`cd ${response.projectName}`)}`);
5706
7271
  if (!response.installDeps) {
5707
- console.log(` ${import_picocolors.default.cyan("bun install")}`);
7272
+ console.log(` ${import_picocolors2.default.cyan("bun install")}`);
5708
7273
  }
5709
- console.log(` ${import_picocolors.default.cyan("bun run dev")}`);
7274
+ console.log(` ${import_picocolors2.default.cyan("bun run dev")}`);
5710
7275
  console.log();
5711
- console.log(` Docs: ${import_picocolors.default.blue("https://github.com/vibeunion/svadmin")}`);
7276
+ console.log(` Docs: ${import_picocolors2.default.blue("https://github.com/vibeunion/svadmin")}`);
5712
7277
  console.log();
5713
7278
  }
5714
7279
  var EJECT_COMPONENTS = [
@@ -5740,64 +7305,64 @@ var EJECT_COMPONENTS = [
5740
7305
  ];
5741
7306
  async function eject(args) {
5742
7307
  console.log();
5743
- console.log(import_picocolors.default.cyan(" svadmin eject") + import_picocolors.default.dim(" — copy internal components for deep customization"));
7308
+ console.log(import_picocolors2.default.cyan(" svadmin eject") + import_picocolors2.default.dim(" — copy internal components for deep customization"));
5744
7309
  console.log();
5745
7310
  const requested = args.filter((a) => !a.startsWith("-"));
5746
7311
  const toEject = requested.length > 0 ? requested.filter((name) => {
5747
7312
  if (!EJECT_COMPONENTS.includes(name)) {
5748
- console.log(import_picocolors.default.yellow(` ⚠ Unknown component: ${name} (skipped)`));
7313
+ console.log(import_picocolors2.default.yellow(` ⚠ Unknown component: ${name} (skipped)`));
5749
7314
  return false;
5750
7315
  }
5751
7316
  return true;
5752
7317
  }) : [...EJECT_COMPONENTS];
5753
7318
  if (toEject.length === 0) {
5754
- console.log(import_picocolors.default.red(" No valid components to eject."));
7319
+ console.log(import_picocolors2.default.red(" No valid components to eject."));
5755
7320
  console.log(` Available: ${EJECT_COMPONENTS.join(", ")}`);
5756
7321
  return;
5757
7322
  }
5758
7323
  let uiSrcDir;
5759
7324
  try {
5760
7325
  const require2 = createRequire2(import.meta.url);
5761
- const uiPkg = path.dirname(require2.resolve("@svadmin/ui/package.json"));
5762
- uiSrcDir = path.join(uiPkg, "src", "components");
7326
+ const uiPkg = path3.dirname(require2.resolve("@svadmin/ui/package.json"));
7327
+ uiSrcDir = path3.join(uiPkg, "src", "components");
5763
7328
  } catch {
5764
- const nm = path.join(process.cwd(), "node_modules", "@svadmin", "ui", "src", "components");
5765
- if (fs.existsSync(nm)) {
7329
+ const nm = path3.join(process.cwd(), "node_modules", "@svadmin", "ui", "src", "components");
7330
+ if (fs3.existsSync(nm)) {
5766
7331
  uiSrcDir = nm;
5767
7332
  } else {
5768
- console.log(import_picocolors.default.red(" ✗ Cannot find @svadmin/ui. Run `bun install` first."));
7333
+ console.log(import_picocolors2.default.red(" ✗ Cannot find @svadmin/ui. Run `bun install` first."));
5769
7334
  return;
5770
7335
  }
5771
7336
  }
5772
- const destDir = path.join(process.cwd(), "src", "components", "svadmin");
5773
- fs.mkdirSync(destDir, { recursive: true });
7337
+ const destDir = path3.join(process.cwd(), "src", "components", "svadmin");
7338
+ fs3.mkdirSync(destDir, { recursive: true });
5774
7339
  let copied = 0;
5775
7340
  for (const name of toEject) {
5776
- const srcFile = path.join(uiSrcDir, `${name}.svelte`);
5777
- const srcFileAlt = path.join(uiSrcDir, "fields", `${name}.svelte`);
5778
- const src = fs.existsSync(srcFile) ? srcFile : fs.existsSync(srcFileAlt) ? srcFileAlt : null;
7341
+ const srcFile = path3.join(uiSrcDir, `${name}.svelte`);
7342
+ const srcFileAlt = path3.join(uiSrcDir, "fields", `${name}.svelte`);
7343
+ const src = fs3.existsSync(srcFile) ? srcFile : fs3.existsSync(srcFileAlt) ? srcFileAlt : null;
5779
7344
  if (!src) {
5780
- console.log(import_picocolors.default.yellow(` ⚠ ${name}.svelte not found in @svadmin/ui (skipped)`));
7345
+ console.log(import_picocolors2.default.yellow(` ⚠ ${name}.svelte not found in @svadmin/ui (skipped)`));
5781
7346
  continue;
5782
7347
  }
5783
- let content = fs.readFileSync(src, "utf-8");
7348
+ let content = fs3.readFileSync(src, "utf-8");
5784
7349
  content = content.replace(/from\s+['"]\.\/ui\//g, "from '@svadmin/ui/components/ui/");
5785
7350
  content = content.replace(/from\s+['"]\.\/((?!ui\/)[^'"]+)['"]/g, "from './$1'");
5786
- const destFile = path.join(destDir, `${name}.svelte`);
5787
- fs.writeFileSync(destFile, content);
5788
- console.log(import_picocolors.default.green(" ✔") + ` ${name}.svelte → src/components/svadmin/`);
7351
+ const destFile = path3.join(destDir, `${name}.svelte`);
7352
+ fs3.writeFileSync(destFile, content);
7353
+ console.log(import_picocolors2.default.green(" ✔") + ` ${name}.svelte → src/components/svadmin/`);
5789
7354
  copied++;
5790
7355
  }
5791
7356
  console.log();
5792
7357
  if (copied > 0) {
5793
- console.log(import_picocolors.default.green(import_picocolors.default.bold(` ✔ Ejected ${copied} component(s)`)));
7358
+ console.log(import_picocolors2.default.green(import_picocolors2.default.bold(` ✔ Ejected ${copied} component(s)`)));
5794
7359
  console.log();
5795
7360
  console.log(" Usage: import overrides in your AdminApp and pass via `components` prop:");
5796
7361
  console.log();
5797
- console.log(import_picocolors.default.dim(' import CustomLayout from "./components/svadmin/Layout.svelte";'));
5798
- console.log(import_picocolors.default.dim(" <AdminApp components={{ Layout: CustomLayout }} ... />"));
7362
+ console.log(import_picocolors2.default.dim(' import CustomLayout from "./components/svadmin/Layout.svelte";'));
7363
+ console.log(import_picocolors2.default.dim(" <AdminApp components={{ Layout: CustomLayout }} ... />"));
5799
7364
  } else {
5800
- console.log(import_picocolors.default.yellow(" No components were ejected."));
7365
+ console.log(import_picocolors2.default.yellow(" No components were ejected."));
5801
7366
  }
5802
7367
  console.log();
5803
7368
  }
@@ -5805,7 +7370,7 @@ var [, , subcommand, ...rest] = process.argv;
5805
7370
  var runCommand = (command) => {
5806
7371
  Promise.resolve().then(command).catch((error) => {
5807
7372
  const message = error instanceof Error ? error.message : String(error);
5808
- console.error(import_picocolors.default.red(`
7373
+ console.error(import_picocolors2.default.red(`
5809
7374
  ✗ ${message}
5810
7375
  `));
5811
7376
  process.exitCode = 2;
@@ -5819,6 +7384,16 @@ if (subcommand === "eject") {
5819
7384
  runCommand(() => upgrade(rest));
5820
7385
  } else if (subcommand === "guidance") {
5821
7386
  runCommand(() => guidance(rest));
7387
+ } else if (subcommand === "infer") {
7388
+ runCommand(() => inferCommand(rest));
7389
+ } else if (subcommand === "lite") {
7390
+ if (rest[0] !== "init") {
7391
+ runCommand(() => {
7392
+ throw new Error("Usage: create-svadmin lite init [project-directory] [--write]");
7393
+ });
7394
+ } else {
7395
+ runCommand(() => liteInitCommand(rest.slice(1)));
7396
+ }
5822
7397
  } else {
5823
7398
  runCommand(init);
5824
7399
  }