@svadmin/create 0.31.0 → 0.33.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/README.md +88 -0
- package/dist/index.js +1128 -124
- package/guidance/AGENTS.md +20 -0
- package/guidance/DESIGN.md +80 -27
- package/package.json +2 -2
- package/scaffold-manifest.json +115 -6
- package/template/src/App.svelte +16 -18
- package/template/src/pages/Login.svelte +192 -20
package/dist/index.js
CHANGED
|
@@ -5024,16 +5024,35 @@ var require_picocolors = __commonJS(function(exports, module) {
|
|
|
5024
5024
|
|
|
5025
5025
|
// src/index.ts
|
|
5026
5026
|
var import_prompts2 = __toESM(require_prompts3(), 1);
|
|
5027
|
-
var
|
|
5028
|
-
import
|
|
5029
|
-
import
|
|
5027
|
+
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
5028
|
+
import fs5 from "node:fs";
|
|
5029
|
+
import path5 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";
|
|
5033
5033
|
|
|
5034
5034
|
// src/project-manifest.ts
|
|
5035
5035
|
import { readFileSync } from "node:fs";
|
|
5036
|
-
var DATA_PROVIDER_CHOICES = [
|
|
5036
|
+
var DATA_PROVIDER_CHOICES = [
|
|
5037
|
+
"simple-rest",
|
|
5038
|
+
"supabase",
|
|
5039
|
+
"graphql",
|
|
5040
|
+
"rest",
|
|
5041
|
+
"airtable",
|
|
5042
|
+
"appwrite",
|
|
5043
|
+
"directus",
|
|
5044
|
+
"drizzle",
|
|
5045
|
+
"elysia",
|
|
5046
|
+
"firebase",
|
|
5047
|
+
"hasura",
|
|
5048
|
+
"medusa",
|
|
5049
|
+
"nestjs-query",
|
|
5050
|
+
"nestjsx-crud",
|
|
5051
|
+
"pocketbase",
|
|
5052
|
+
"sanity",
|
|
5053
|
+
"strapi",
|
|
5054
|
+
"none"
|
|
5055
|
+
];
|
|
5037
5056
|
var AUTH_PROVIDER_CHOICES = ["mock", "jwt", "supabase", "none"];
|
|
5038
5057
|
function assertJsonObject(candidate, path) {
|
|
5039
5058
|
if (candidate === null || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
@@ -6898,11 +6917,695 @@ Dry run only; re-run with --write to add missing Lite routes.`);
|
|
|
6898
6917
|
Written ${result.written.length} file(s); preserved ${result.preserved.length} existing file(s).`);
|
|
6899
6918
|
}
|
|
6900
6919
|
|
|
6920
|
+
// src/init-arguments.ts
|
|
6921
|
+
var PRESET_NAMES = ["supabase", "rest", "graphql"];
|
|
6922
|
+
var INIT_PRESETS = {
|
|
6923
|
+
supabase: { dataProvider: "supabase", authProvider: "supabase" },
|
|
6924
|
+
rest: { dataProvider: "simple-rest", authProvider: "jwt" },
|
|
6925
|
+
graphql: { dataProvider: "graphql", authProvider: "mock" }
|
|
6926
|
+
};
|
|
6927
|
+
function isPresetName(value) {
|
|
6928
|
+
return PRESET_NAMES.includes(value);
|
|
6929
|
+
}
|
|
6930
|
+
function isDataProviderChoice(value) {
|
|
6931
|
+
return DATA_PROVIDER_CHOICES.includes(value);
|
|
6932
|
+
}
|
|
6933
|
+
function isAuthProviderChoice(value) {
|
|
6934
|
+
return AUTH_PROVIDER_CHOICES.includes(value);
|
|
6935
|
+
}
|
|
6936
|
+
function takeValue(args, index, option) {
|
|
6937
|
+
const value = args[index + 1];
|
|
6938
|
+
if (value === undefined || value.startsWith("-")) {
|
|
6939
|
+
throw new Error(`${option} requires a value`);
|
|
6940
|
+
}
|
|
6941
|
+
return value;
|
|
6942
|
+
}
|
|
6943
|
+
function parseInitArguments(args) {
|
|
6944
|
+
let projectName;
|
|
6945
|
+
let preset;
|
|
6946
|
+
let dataProvider;
|
|
6947
|
+
let authProvider;
|
|
6948
|
+
let installDependencies;
|
|
6949
|
+
for (let index = 0;index < args.length; index++) {
|
|
6950
|
+
const argument = args[index];
|
|
6951
|
+
if (argument === undefined)
|
|
6952
|
+
continue;
|
|
6953
|
+
if (argument === "--preset") {
|
|
6954
|
+
const value = takeValue(args, index, "--preset");
|
|
6955
|
+
if (!isPresetName(value)) {
|
|
6956
|
+
throw new Error(`Unknown preset "${value}"; expected one of: ${PRESET_NAMES.join(", ")}`);
|
|
6957
|
+
}
|
|
6958
|
+
preset = value;
|
|
6959
|
+
index++;
|
|
6960
|
+
} else if (argument === "--data-provider") {
|
|
6961
|
+
const value = takeValue(args, index, "--data-provider");
|
|
6962
|
+
if (!isDataProviderChoice(value)) {
|
|
6963
|
+
throw new Error(`Unknown data provider "${value}"; expected one of: ${DATA_PROVIDER_CHOICES.join(", ")}`);
|
|
6964
|
+
}
|
|
6965
|
+
dataProvider = value;
|
|
6966
|
+
index++;
|
|
6967
|
+
} else if (argument === "--auth-provider") {
|
|
6968
|
+
const value = takeValue(args, index, "--auth-provider");
|
|
6969
|
+
if (!isAuthProviderChoice(value)) {
|
|
6970
|
+
throw new Error(`Unknown auth provider "${value}"; expected one of: ${AUTH_PROVIDER_CHOICES.join(", ")}`);
|
|
6971
|
+
}
|
|
6972
|
+
authProvider = value;
|
|
6973
|
+
index++;
|
|
6974
|
+
} else if (argument === "--install") {
|
|
6975
|
+
installDependencies = true;
|
|
6976
|
+
} else if (argument === "--no-install") {
|
|
6977
|
+
installDependencies = false;
|
|
6978
|
+
} else if (argument.startsWith("-")) {
|
|
6979
|
+
throw new Error(`Unknown option: ${argument}`);
|
|
6980
|
+
} else if (projectName === undefined) {
|
|
6981
|
+
projectName = argument;
|
|
6982
|
+
} else {
|
|
6983
|
+
throw new Error(`Unexpected argument: ${argument}`);
|
|
6984
|
+
}
|
|
6985
|
+
}
|
|
6986
|
+
const resolved = {};
|
|
6987
|
+
if (projectName !== undefined)
|
|
6988
|
+
resolved.projectName = projectName;
|
|
6989
|
+
if (preset !== undefined)
|
|
6990
|
+
resolved.preset = preset;
|
|
6991
|
+
if (dataProvider !== undefined)
|
|
6992
|
+
resolved.dataProvider = dataProvider;
|
|
6993
|
+
if (authProvider !== undefined)
|
|
6994
|
+
resolved.authProvider = authProvider;
|
|
6995
|
+
if (installDependencies !== undefined)
|
|
6996
|
+
resolved.installDependencies = installDependencies;
|
|
6997
|
+
return resolved;
|
|
6998
|
+
}
|
|
6999
|
+
function resolvePresetSelections(args) {
|
|
7000
|
+
const preset = args.preset === undefined ? undefined : INIT_PRESETS[args.preset];
|
|
7001
|
+
const dataProvider = args.dataProvider ?? preset?.dataProvider;
|
|
7002
|
+
const authProvider = args.authProvider ?? preset?.authProvider;
|
|
7003
|
+
if (dataProvider === undefined || authProvider === undefined)
|
|
7004
|
+
return;
|
|
7005
|
+
return { dataProvider, authProvider };
|
|
7006
|
+
}
|
|
7007
|
+
|
|
7008
|
+
// src/add-command.ts
|
|
7009
|
+
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
7010
|
+
import fs4 from "node:fs";
|
|
7011
|
+
import path4 from "node:path";
|
|
7012
|
+
|
|
7013
|
+
// src/scaffold-platform.ts
|
|
7014
|
+
import { existsSync, readFileSync as readFileSync2 } from "node:fs";
|
|
7015
|
+
import { join } from "node:path";
|
|
7016
|
+
var SCAFFOLD_CONFIG_PATH = "src/svadmin.config.ts";
|
|
7017
|
+
var ADMIN_AI_MANIFEST_FILENAME = "svadmin.ai.json";
|
|
7018
|
+
var ADMIN_SCHEMA_FILENAME = "svadmin.schema.json";
|
|
7019
|
+
var SCAFFOLD_OFFICIAL_PROVIDERS = [
|
|
7020
|
+
{ name: "simple-rest", package: "@svadmin/simple-rest", capabilities: ["data", "jwt-auth", "session"], stability: "stable" },
|
|
7021
|
+
{ name: "supabase", package: "@svadmin/supabase", capabilities: ["data", "auth", "live", "storage", "audit"], stability: "stable" },
|
|
7022
|
+
{ name: "graphql", package: "@svadmin/graphql", capabilities: ["data", "graphql"], stability: "stable" },
|
|
7023
|
+
{ name: "rest", package: "@svadmin/rest", capabilities: ["data", "rest", "custom-endpoints"], stability: "experimental" },
|
|
7024
|
+
{ name: "airtable", package: "@svadmin/airtable", capabilities: ["data"], stability: "stable" },
|
|
7025
|
+
{ name: "appwrite", package: "@svadmin/appwrite", capabilities: ["data", "auth", "storage", "live"], stability: "stable" },
|
|
7026
|
+
{ name: "directus", package: "@svadmin/directus", capabilities: ["data", "auth"], stability: "stable" },
|
|
7027
|
+
{ name: "drizzle", package: "@svadmin/drizzle", capabilities: ["server-data", "migrations"], stability: "stable" },
|
|
7028
|
+
{ name: "elysia", package: "@svadmin/elysia", capabilities: ["server-data"], stability: "stable" },
|
|
7029
|
+
{ name: "firebase", package: "@svadmin/firebase", capabilities: ["data", "auth", "storage", "live"], stability: "stable" },
|
|
7030
|
+
{ name: "hasura", package: "@svadmin/hasura", capabilities: ["data", "graphql", "live"], stability: "stable" },
|
|
7031
|
+
{ name: "medusa", package: "@svadmin/medusa", capabilities: ["data", "auth"], stability: "stable" },
|
|
7032
|
+
{ name: "nestjs-query", package: "@svadmin/nestjs-query", capabilities: ["data", "graphql"], stability: "stable" },
|
|
7033
|
+
{ name: "nestjsx-crud", package: "@svadmin/nestjsx-crud", capabilities: ["data"], stability: "stable" },
|
|
7034
|
+
{ name: "pocketbase", package: "@svadmin/pocketbase", capabilities: ["data", "auth", "storage", "live"], stability: "stable" },
|
|
7035
|
+
{ name: "sanity", package: "@svadmin/sanity", capabilities: ["data", "live"], stability: "stable" },
|
|
7036
|
+
{ name: "strapi", package: "@svadmin/strapi", capabilities: ["data", "auth"], stability: "stable" },
|
|
7037
|
+
{ name: "refine-adapter", package: "@svadmin/refine-adapter", capabilities: ["adapter", "refine-bridge"], stability: "stable" },
|
|
7038
|
+
{ name: "sso", package: "@svadmin/sso", capabilities: ["auth", "oidc", "oauth2"], stability: "stable" },
|
|
7039
|
+
{ name: "sveltekit", package: "@svadmin/sveltekit", capabilities: ["router", "ssr"], stability: "stable" }
|
|
7040
|
+
];
|
|
7041
|
+
var SCAFFOLD_DATA_PROVIDERS = {
|
|
7042
|
+
"simple-rest": {
|
|
7043
|
+
package: "@svadmin/simple-rest",
|
|
7044
|
+
capabilities: ["data", "filter", "sort", "pagination"]
|
|
7045
|
+
},
|
|
7046
|
+
supabase: {
|
|
7047
|
+
package: "@svadmin/supabase",
|
|
7048
|
+
capabilities: ["data", "auth", "live", "storage"]
|
|
7049
|
+
},
|
|
7050
|
+
graphql: {
|
|
7051
|
+
package: "@svadmin/graphql",
|
|
7052
|
+
capabilities: ["data", "graphql"]
|
|
7053
|
+
},
|
|
7054
|
+
rest: {
|
|
7055
|
+
package: "@svadmin/rest",
|
|
7056
|
+
capabilities: ["data", "rest", "custom-endpoints"]
|
|
7057
|
+
},
|
|
7058
|
+
airtable: {
|
|
7059
|
+
package: "@svadmin/airtable",
|
|
7060
|
+
capabilities: ["data"]
|
|
7061
|
+
},
|
|
7062
|
+
appwrite: {
|
|
7063
|
+
package: "@svadmin/appwrite",
|
|
7064
|
+
capabilities: ["data", "auth", "storage", "live"]
|
|
7065
|
+
},
|
|
7066
|
+
directus: {
|
|
7067
|
+
package: "@svadmin/directus",
|
|
7068
|
+
capabilities: ["data", "auth"]
|
|
7069
|
+
},
|
|
7070
|
+
drizzle: {
|
|
7071
|
+
package: "@svadmin/drizzle",
|
|
7072
|
+
capabilities: ["server-data", "migrations"]
|
|
7073
|
+
},
|
|
7074
|
+
elysia: {
|
|
7075
|
+
package: "@svadmin/elysia",
|
|
7076
|
+
capabilities: ["server-data"]
|
|
7077
|
+
},
|
|
7078
|
+
firebase: {
|
|
7079
|
+
package: "@svadmin/firebase",
|
|
7080
|
+
capabilities: ["data", "auth", "storage", "live"]
|
|
7081
|
+
},
|
|
7082
|
+
hasura: {
|
|
7083
|
+
package: "@svadmin/hasura",
|
|
7084
|
+
capabilities: ["data", "graphql", "live"]
|
|
7085
|
+
},
|
|
7086
|
+
medusa: {
|
|
7087
|
+
package: "@svadmin/medusa",
|
|
7088
|
+
capabilities: ["data", "auth"]
|
|
7089
|
+
},
|
|
7090
|
+
"nestjs-query": {
|
|
7091
|
+
package: "@svadmin/nestjs-query",
|
|
7092
|
+
capabilities: ["data", "graphql"]
|
|
7093
|
+
},
|
|
7094
|
+
"nestjsx-crud": {
|
|
7095
|
+
package: "@svadmin/nestjsx-crud",
|
|
7096
|
+
capabilities: ["data"]
|
|
7097
|
+
},
|
|
7098
|
+
pocketbase: {
|
|
7099
|
+
package: "@svadmin/pocketbase",
|
|
7100
|
+
capabilities: ["data", "auth", "storage", "live"]
|
|
7101
|
+
},
|
|
7102
|
+
sanity: {
|
|
7103
|
+
package: "@svadmin/sanity",
|
|
7104
|
+
capabilities: ["data", "live"]
|
|
7105
|
+
},
|
|
7106
|
+
strapi: {
|
|
7107
|
+
package: "@svadmin/strapi",
|
|
7108
|
+
capabilities: ["data", "auth"]
|
|
7109
|
+
},
|
|
7110
|
+
none: {
|
|
7111
|
+
package: null,
|
|
7112
|
+
capabilities: ["custom"]
|
|
7113
|
+
}
|
|
7114
|
+
};
|
|
7115
|
+
var SCAFFOLD_AUTH_PROVIDERS = {
|
|
7116
|
+
mock: {
|
|
7117
|
+
package: null,
|
|
7118
|
+
capabilities: ["demo-auth"]
|
|
7119
|
+
},
|
|
7120
|
+
jwt: {
|
|
7121
|
+
package: "@svadmin/simple-rest",
|
|
7122
|
+
capabilities: ["jwt-auth", "session"]
|
|
7123
|
+
},
|
|
7124
|
+
supabase: {
|
|
7125
|
+
package: "@svadmin/supabase",
|
|
7126
|
+
capabilities: ["auth", "session"]
|
|
7127
|
+
},
|
|
7128
|
+
none: {
|
|
7129
|
+
package: null,
|
|
7130
|
+
capabilities: []
|
|
7131
|
+
}
|
|
7132
|
+
};
|
|
7133
|
+
var SCAFFOLD_RESOURCES = [
|
|
7134
|
+
{
|
|
7135
|
+
name: "posts",
|
|
7136
|
+
label: "Posts",
|
|
7137
|
+
operations: ["list", "create", "edit", "show", "delete"],
|
|
7138
|
+
fields: [
|
|
7139
|
+
{ key: "id", label: "ID", type: "number", required: true },
|
|
7140
|
+
{ key: "title", label: "Title", type: "text", required: false },
|
|
7141
|
+
{ key: "body", label: "Body", type: "textarea", required: false },
|
|
7142
|
+
{ key: "userId", label: "Author", type: "number", required: false }
|
|
7143
|
+
]
|
|
7144
|
+
},
|
|
7145
|
+
{
|
|
7146
|
+
name: "users",
|
|
7147
|
+
label: "Users",
|
|
7148
|
+
operations: ["list", "show"],
|
|
7149
|
+
fields: [
|
|
7150
|
+
{ key: "id", label: "ID", type: "number", required: true },
|
|
7151
|
+
{ key: "name", label: "Name", type: "text", required: false },
|
|
7152
|
+
{ key: "username", label: "Username", type: "text", required: false },
|
|
7153
|
+
{ key: "email", label: "Email", type: "email", required: false },
|
|
7154
|
+
{ key: "phone", label: "Phone", type: "phone", required: false },
|
|
7155
|
+
{ key: "website", label: "Website", type: "url", required: false }
|
|
7156
|
+
]
|
|
7157
|
+
},
|
|
7158
|
+
{
|
|
7159
|
+
name: "comments",
|
|
7160
|
+
label: "Comments",
|
|
7161
|
+
operations: ["list", "create", "edit", "show", "delete"],
|
|
7162
|
+
fields: [
|
|
7163
|
+
{ key: "id", label: "ID", type: "number", required: true },
|
|
7164
|
+
{ key: "postId", label: "Post", type: "number", required: false },
|
|
7165
|
+
{ key: "name", label: "Title", type: "text", required: false },
|
|
7166
|
+
{ key: "email", label: "Email", type: "email", required: false },
|
|
7167
|
+
{ key: "body", label: "Body", type: "textarea", required: false }
|
|
7168
|
+
]
|
|
7169
|
+
},
|
|
7170
|
+
{
|
|
7171
|
+
name: "todos",
|
|
7172
|
+
label: "Todos",
|
|
7173
|
+
operations: ["list", "create", "edit", "show", "delete"],
|
|
7174
|
+
fields: [
|
|
7175
|
+
{ key: "id", label: "ID", type: "number", required: true },
|
|
7176
|
+
{ key: "title", label: "Title", type: "text", required: false },
|
|
7177
|
+
{ key: "completed", label: "Done", type: "boolean", required: false },
|
|
7178
|
+
{ key: "userId", label: "User", type: "number", required: false }
|
|
7179
|
+
]
|
|
7180
|
+
}
|
|
7181
|
+
];
|
|
7182
|
+
var SCAFFOLD_MIGRATION_NOTES = [
|
|
7183
|
+
"Run `create-svadmin doctor` after changing providers or dependencies.",
|
|
7184
|
+
"Apply dependency migrations with `create-svadmin migrate --write`.",
|
|
7185
|
+
"Keep src/svadmin.config.ts and svadmin.ai.json in sync."
|
|
7186
|
+
];
|
|
7187
|
+
var SCAFFOLD_UI_COMPONENTS = [
|
|
7188
|
+
...[
|
|
7189
|
+
"AdminApp",
|
|
7190
|
+
"AutoTable",
|
|
7191
|
+
"AutoForm",
|
|
7192
|
+
"ShowPage",
|
|
7193
|
+
"Layout",
|
|
7194
|
+
"Sidebar",
|
|
7195
|
+
"Header",
|
|
7196
|
+
"CommandPalette",
|
|
7197
|
+
"DataState",
|
|
7198
|
+
"CreateButton",
|
|
7199
|
+
"EditButton",
|
|
7200
|
+
"DeleteButton",
|
|
7201
|
+
"ShowButton",
|
|
7202
|
+
"RefreshButton",
|
|
7203
|
+
"ExportButton",
|
|
7204
|
+
"ImportButton",
|
|
7205
|
+
"FilterBuilder",
|
|
7206
|
+
"ConfigErrorScreen",
|
|
7207
|
+
"AppFooter",
|
|
7208
|
+
"ResizableGrid",
|
|
7209
|
+
"AreaChart",
|
|
7210
|
+
"ScatterChart",
|
|
7211
|
+
"HighlightText",
|
|
7212
|
+
"Marquee",
|
|
7213
|
+
"MediaPlayer",
|
|
7214
|
+
"TopProgressBar",
|
|
7215
|
+
"PhoneInput"
|
|
7216
|
+
].map((name) => ({ name, package: "@svadmin/ui", import: "@svadmin/ui" })),
|
|
7217
|
+
{ name: "CodeEditor", package: "@svadmin/ui", import: "@svadmin/ui/code-editor" },
|
|
7218
|
+
{ name: "CodeEditorPresets", package: "@svadmin/ui", import: "@svadmin/ui/code-editor/presets" },
|
|
7219
|
+
{ name: "JsonEditor", package: "@svadmin/ui", import: "@svadmin/ui/json-editor" },
|
|
7220
|
+
{ name: "QRCode", package: "@svadmin/ui", import: "@svadmin/ui/qr-code" }
|
|
7221
|
+
];
|
|
7222
|
+
var CUSTOM_DATA_PROVIDER_BODY = [
|
|
7223
|
+
"const unsupported = (): never => {",
|
|
7224
|
+
" throw new Error('Configure a DataProvider in src/svadmin.config.ts before using the app.');",
|
|
7225
|
+
"};",
|
|
7226
|
+
"const dataProvider: DataProvider = {",
|
|
7227
|
+
" getApiUrl: () => '',",
|
|
7228
|
+
" getList: async () => unsupported(),",
|
|
7229
|
+
" getOne: async () => unsupported(),",
|
|
7230
|
+
" create: async () => unsupported(),",
|
|
7231
|
+
" update: async () => unsupported(),",
|
|
7232
|
+
" deleteOne: async () => unsupported(),",
|
|
7233
|
+
"};"
|
|
7234
|
+
];
|
|
7235
|
+
function quote(value) {
|
|
7236
|
+
return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'")}'`;
|
|
7237
|
+
}
|
|
7238
|
+
function buildSvadminConfigSource(options) {
|
|
7239
|
+
const imports = new Set(["import { createProviderBundle, defineAdminConfig } from '@svadmin/app';"]);
|
|
7240
|
+
const body = [];
|
|
7241
|
+
let needsSupabaseClient = false;
|
|
7242
|
+
switch (options.dataProvider) {
|
|
7243
|
+
case "simple-rest":
|
|
7244
|
+
imports.add("import { createSimpleRestDataProvider } from '@svadmin/simple-rest';");
|
|
7245
|
+
body.push("const dataProvider = await createSimpleRestDataProvider('https://jsonplaceholder.typicode.com');");
|
|
7246
|
+
break;
|
|
7247
|
+
case "supabase":
|
|
7248
|
+
imports.add("import { createSupabaseDataProvider } from '@svadmin/supabase';");
|
|
7249
|
+
needsSupabaseClient = true;
|
|
7250
|
+
body.push("const dataProvider = createSupabaseDataProvider(supabaseClient);");
|
|
7251
|
+
break;
|
|
7252
|
+
case "graphql":
|
|
7253
|
+
imports.add("import { createGraphQLDataProvider } from '@svadmin/graphql';");
|
|
7254
|
+
body.push("const dataProvider = await createGraphQLDataProvider('https://example.com/graphql');");
|
|
7255
|
+
break;
|
|
7256
|
+
case "none":
|
|
7257
|
+
imports.add("import type { DataProvider } from '@svadmin/core';");
|
|
7258
|
+
body.push(...CUSTOM_DATA_PROVIDER_BODY);
|
|
7259
|
+
break;
|
|
7260
|
+
default: {
|
|
7261
|
+
const descriptor = SCAFFOLD_DATA_PROVIDERS[options.dataProvider];
|
|
7262
|
+
const packageName = descriptor.package ?? "@svadmin/core";
|
|
7263
|
+
imports.add("import type { DataProvider } from '@svadmin/core';");
|
|
7264
|
+
body.push(`// TODO: configure ${packageName} in src/svadmin.config.ts.`, "async function createDataProvider(): Promise<DataProvider> {", ` throw new Error('Configure the ${options.dataProvider} data provider before running the app.');`, "}", "const dataProvider = await createDataProvider();");
|
|
7265
|
+
break;
|
|
7266
|
+
}
|
|
7267
|
+
}
|
|
7268
|
+
let authExpression = null;
|
|
7269
|
+
switch (options.authProvider) {
|
|
7270
|
+
case "mock":
|
|
7271
|
+
imports.add("import { mockAuthProvider } from './providers/mockAuth';");
|
|
7272
|
+
authExpression = "mockAuthProvider";
|
|
7273
|
+
break;
|
|
7274
|
+
case "jwt":
|
|
7275
|
+
imports.add("import { createSimpleRestAuthProvider } from '@svadmin/simple-rest';");
|
|
7276
|
+
body.push("const authProvider = createSimpleRestAuthProvider({ loginUrl: '/api/auth/login', identityUrl: '/api/auth/me' });");
|
|
7277
|
+
authExpression = "authProvider";
|
|
7278
|
+
break;
|
|
7279
|
+
case "supabase":
|
|
7280
|
+
imports.add("import { createSupabaseAuthProvider } from '@svadmin/supabase';");
|
|
7281
|
+
needsSupabaseClient = true;
|
|
7282
|
+
body.push("const authProvider = createSupabaseAuthProvider(supabaseClient);");
|
|
7283
|
+
authExpression = "authProvider";
|
|
7284
|
+
break;
|
|
7285
|
+
case "none":
|
|
7286
|
+
break;
|
|
7287
|
+
}
|
|
7288
|
+
if (needsSupabaseClient) {
|
|
7289
|
+
imports.add("import { supabaseClient } from './providers/supabase';");
|
|
7290
|
+
body.unshift("if (!supabaseClient) throw new Error('Supabase is not configured: set VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY.');");
|
|
7291
|
+
}
|
|
7292
|
+
imports.add("import { resources } from './resources';");
|
|
7293
|
+
const providerEntries = [" dataProvider,", ...authExpression === null ? [] : [` authProvider: ${authExpression},`]].join(`
|
|
7294
|
+
`);
|
|
7295
|
+
return [
|
|
7296
|
+
"/**",
|
|
7297
|
+
" * svadmin application entrypoint — the first file AI tooling should read.",
|
|
7298
|
+
" * Generated by create-svadmin; edit freely.",
|
|
7299
|
+
" */",
|
|
7300
|
+
[...imports].join(`
|
|
7301
|
+
`),
|
|
7302
|
+
"",
|
|
7303
|
+
body.join(`
|
|
7304
|
+
`),
|
|
7305
|
+
"",
|
|
7306
|
+
"export default defineAdminConfig({",
|
|
7307
|
+
` name: ${quote(options.projectName)},`,
|
|
7308
|
+
" providers: createProviderBundle({",
|
|
7309
|
+
providerEntries,
|
|
7310
|
+
" }),",
|
|
7311
|
+
" resources,",
|
|
7312
|
+
"});",
|
|
7313
|
+
""
|
|
7314
|
+
].join(`
|
|
7315
|
+
`);
|
|
7316
|
+
}
|
|
7317
|
+
function buildAdminAiManifest(options) {
|
|
7318
|
+
return {
|
|
7319
|
+
$schema: `./${ADMIN_SCHEMA_FILENAME}`,
|
|
7320
|
+
version: 1,
|
|
7321
|
+
project: {
|
|
7322
|
+
name: options.projectName,
|
|
7323
|
+
entrypoints: {
|
|
7324
|
+
config: SCAFFOLD_CONFIG_PATH,
|
|
7325
|
+
manifest: ADMIN_AI_MANIFEST_FILENAME
|
|
7326
|
+
},
|
|
7327
|
+
commands: {
|
|
7328
|
+
dev: "bun run dev",
|
|
7329
|
+
build: "bun run build",
|
|
7330
|
+
preview: "bun run preview",
|
|
7331
|
+
check: "bun run check"
|
|
7332
|
+
},
|
|
7333
|
+
testCommand: null,
|
|
7334
|
+
forbiddenImports: ["@svadmin/ui/src", "@svadmin/core/src", "@svadmin/core/dist"]
|
|
7335
|
+
},
|
|
7336
|
+
providers: {
|
|
7337
|
+
data: { choice: options.dataProvider, ...SCAFFOLD_DATA_PROVIDERS[options.dataProvider] },
|
|
7338
|
+
auth: { choice: options.authProvider, ...SCAFFOLD_AUTH_PROVIDERS[options.authProvider] }
|
|
7339
|
+
},
|
|
7340
|
+
resources: SCAFFOLD_RESOURCES,
|
|
7341
|
+
routes: SCAFFOLD_RESOURCES.map((resource) => ({
|
|
7342
|
+
resource: resource.name,
|
|
7343
|
+
path: `/${resource.name}`,
|
|
7344
|
+
operations: resource.operations
|
|
7345
|
+
})),
|
|
7346
|
+
components: SCAFFOLD_UI_COMPONENTS,
|
|
7347
|
+
providerCatalog: SCAFFOLD_OFFICIAL_PROVIDERS,
|
|
7348
|
+
migration: {
|
|
7349
|
+
scaffoldVersion: options.scaffoldVersion ?? "0.0.0",
|
|
7350
|
+
coreVersionRange: options.coreVersionRange ?? null,
|
|
7351
|
+
notes: SCAFFOLD_MIGRATION_NOTES
|
|
7352
|
+
},
|
|
7353
|
+
plugins: [],
|
|
7354
|
+
guidance: ["AGENTS.md", "DESIGN.md"]
|
|
7355
|
+
};
|
|
7356
|
+
}
|
|
7357
|
+
function buildScaffoldPlatformFiles(options) {
|
|
7358
|
+
return {
|
|
7359
|
+
config: {
|
|
7360
|
+
path: SCAFFOLD_CONFIG_PATH,
|
|
7361
|
+
content: buildSvadminConfigSource(options)
|
|
7362
|
+
},
|
|
7363
|
+
aiManifest: {
|
|
7364
|
+
path: ADMIN_AI_MANIFEST_FILENAME,
|
|
7365
|
+
content: `${JSON.stringify(buildAdminAiManifest(options), null, 2)}
|
|
7366
|
+
`
|
|
7367
|
+
},
|
|
7368
|
+
schema: {
|
|
7369
|
+
path: ADMIN_SCHEMA_FILENAME,
|
|
7370
|
+
content: `${JSON.stringify(buildAdminSchemaJson(), null, 2)}
|
|
7371
|
+
`
|
|
7372
|
+
}
|
|
7373
|
+
};
|
|
7374
|
+
}
|
|
7375
|
+
function buildAdminSchemaJson() {
|
|
7376
|
+
const provider = {
|
|
7377
|
+
type: "object",
|
|
7378
|
+
required: ["choice", "package", "capabilities"],
|
|
7379
|
+
properties: {
|
|
7380
|
+
choice: { type: "string" },
|
|
7381
|
+
package: { type: ["string", "null"] },
|
|
7382
|
+
capabilities: { type: "array", items: { type: "string" } }
|
|
7383
|
+
}
|
|
7384
|
+
};
|
|
7385
|
+
const field = {
|
|
7386
|
+
type: "object",
|
|
7387
|
+
required: ["key", "label", "type", "required"],
|
|
7388
|
+
properties: {
|
|
7389
|
+
key: { type: "string" },
|
|
7390
|
+
label: { type: "string" },
|
|
7391
|
+
type: { type: "string" },
|
|
7392
|
+
required: { type: "boolean" }
|
|
7393
|
+
}
|
|
7394
|
+
};
|
|
7395
|
+
const resource = {
|
|
7396
|
+
type: "object",
|
|
7397
|
+
required: ["name", "label", "fields", "operations"],
|
|
7398
|
+
properties: {
|
|
7399
|
+
name: { type: "string" },
|
|
7400
|
+
label: { type: "string" },
|
|
7401
|
+
fields: { type: "array", items: field },
|
|
7402
|
+
operations: { type: "array", items: { type: "string" } }
|
|
7403
|
+
}
|
|
7404
|
+
};
|
|
7405
|
+
return {
|
|
7406
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
7407
|
+
$id: "https://svadmin.dev/schemas/svadmin.ai.json",
|
|
7408
|
+
title: "svadmin AI manifest",
|
|
7409
|
+
type: "object",
|
|
7410
|
+
required: ["version", "project", "providers", "resources"],
|
|
7411
|
+
properties: {
|
|
7412
|
+
$schema: { type: "string" },
|
|
7413
|
+
version: { const: 1 },
|
|
7414
|
+
project: {
|
|
7415
|
+
type: "object",
|
|
7416
|
+
required: ["name", "entrypoints", "commands"],
|
|
7417
|
+
properties: {
|
|
7418
|
+
name: { type: "string" },
|
|
7419
|
+
entrypoints: {
|
|
7420
|
+
type: "object",
|
|
7421
|
+
required: ["config", "manifest"],
|
|
7422
|
+
properties: { config: { type: "string" }, manifest: { type: "string" } }
|
|
7423
|
+
},
|
|
7424
|
+
commands: { type: "object", additionalProperties: { type: "string" } },
|
|
7425
|
+
testCommand: { type: ["string", "null"] },
|
|
7426
|
+
forbiddenImports: { type: "array", items: { type: "string" } }
|
|
7427
|
+
}
|
|
7428
|
+
},
|
|
7429
|
+
providers: {
|
|
7430
|
+
type: "object",
|
|
7431
|
+
required: ["data", "auth"],
|
|
7432
|
+
properties: { data: provider, auth: provider }
|
|
7433
|
+
},
|
|
7434
|
+
resources: { type: "array", items: resource },
|
|
7435
|
+
routes: {
|
|
7436
|
+
type: "array",
|
|
7437
|
+
items: {
|
|
7438
|
+
type: "object",
|
|
7439
|
+
required: ["resource", "path", "operations"],
|
|
7440
|
+
properties: {
|
|
7441
|
+
resource: { type: "string" },
|
|
7442
|
+
path: { type: "string" },
|
|
7443
|
+
operations: { type: "array", items: { type: "string" } }
|
|
7444
|
+
}
|
|
7445
|
+
}
|
|
7446
|
+
},
|
|
7447
|
+
components: {
|
|
7448
|
+
type: "array",
|
|
7449
|
+
items: {
|
|
7450
|
+
type: "object",
|
|
7451
|
+
required: ["name", "package", "import"],
|
|
7452
|
+
properties: {
|
|
7453
|
+
name: { type: "string" },
|
|
7454
|
+
package: { type: "string" },
|
|
7455
|
+
import: { type: "string" }
|
|
7456
|
+
}
|
|
7457
|
+
}
|
|
7458
|
+
},
|
|
7459
|
+
providerCatalog: {
|
|
7460
|
+
type: "array",
|
|
7461
|
+
items: {
|
|
7462
|
+
type: "object",
|
|
7463
|
+
required: ["name", "package", "capabilities", "stability"],
|
|
7464
|
+
properties: {
|
|
7465
|
+
name: { type: "string" },
|
|
7466
|
+
package: { type: "string" },
|
|
7467
|
+
capabilities: { type: "array", items: { type: "string" } },
|
|
7468
|
+
stability: { type: "string" }
|
|
7469
|
+
}
|
|
7470
|
+
}
|
|
7471
|
+
},
|
|
7472
|
+
migration: {
|
|
7473
|
+
type: "object",
|
|
7474
|
+
required: ["scaffoldVersion", "notes"],
|
|
7475
|
+
properties: {
|
|
7476
|
+
scaffoldVersion: { type: "string" },
|
|
7477
|
+
coreVersionRange: { type: ["string", "null"] },
|
|
7478
|
+
notes: { type: "array", items: { type: "string" } }
|
|
7479
|
+
}
|
|
7480
|
+
},
|
|
7481
|
+
plugins: { type: "array" },
|
|
7482
|
+
guidance: { type: "array", items: { type: "string" } }
|
|
7483
|
+
}
|
|
7484
|
+
};
|
|
7485
|
+
}
|
|
7486
|
+
function parseManifest(path) {
|
|
7487
|
+
let parsed;
|
|
7488
|
+
try {
|
|
7489
|
+
parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
7490
|
+
} catch {
|
|
7491
|
+
return {
|
|
7492
|
+
issue: {
|
|
7493
|
+
path,
|
|
7494
|
+
message: "svadmin.ai.json is not valid JSON",
|
|
7495
|
+
action: "regenerate it or run `create-svadmin doctor` after fixing the file"
|
|
7496
|
+
}
|
|
7497
|
+
};
|
|
7498
|
+
}
|
|
7499
|
+
if (typeof parsed !== "object" || parsed === null || Reflect.get(parsed, "version") !== 1) {
|
|
7500
|
+
return {
|
|
7501
|
+
issue: {
|
|
7502
|
+
path,
|
|
7503
|
+
message: "svadmin.ai.json must be an object with version 1",
|
|
7504
|
+
action: "regenerate the project platform files"
|
|
7505
|
+
}
|
|
7506
|
+
};
|
|
7507
|
+
}
|
|
7508
|
+
return { manifest: parsed };
|
|
7509
|
+
}
|
|
7510
|
+
function declaredDependencies(project) {
|
|
7511
|
+
return new Set([
|
|
7512
|
+
...Object.keys(project.dependencies ?? {}),
|
|
7513
|
+
...Object.keys(project.devDependencies ?? {})
|
|
7514
|
+
]);
|
|
7515
|
+
}
|
|
7516
|
+
function readInstalledProviderMeta(projectDirectory, packageName) {
|
|
7517
|
+
const manifestPath = join(projectDirectory, "node_modules", ...packageName.split("/"), "package.json");
|
|
7518
|
+
if (!existsSync(manifestPath))
|
|
7519
|
+
return null;
|
|
7520
|
+
try {
|
|
7521
|
+
const parsed = JSON.parse(readFileSync2(manifestPath, "utf8"));
|
|
7522
|
+
const meta = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "svadmin") : undefined;
|
|
7523
|
+
return typeof meta === "object" && meta !== null ? meta : null;
|
|
7524
|
+
} catch {
|
|
7525
|
+
return null;
|
|
7526
|
+
}
|
|
7527
|
+
}
|
|
7528
|
+
function firstVersion(range) {
|
|
7529
|
+
const match = /(\d+)\.(\d+)\.(\d+)/u.exec(range);
|
|
7530
|
+
if (match === null)
|
|
7531
|
+
return null;
|
|
7532
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
7533
|
+
}
|
|
7534
|
+
function coreRangesCompatible(projectRange, moduleRange) {
|
|
7535
|
+
const project = firstVersion(projectRange);
|
|
7536
|
+
const module = firstVersion(moduleRange);
|
|
7537
|
+
if (project === null || module === null)
|
|
7538
|
+
return true;
|
|
7539
|
+
if (project[0] !== module[0])
|
|
7540
|
+
return false;
|
|
7541
|
+
return project[0] !== 0 || project[1] === module[1];
|
|
7542
|
+
}
|
|
7543
|
+
function checkAdminManifest(projectDirectory, project) {
|
|
7544
|
+
const configPath = join(projectDirectory, SCAFFOLD_CONFIG_PATH);
|
|
7545
|
+
if (!existsSync(configPath))
|
|
7546
|
+
return [];
|
|
7547
|
+
const manifestPath = join(projectDirectory, ADMIN_AI_MANIFEST_FILENAME);
|
|
7548
|
+
if (!existsSync(manifestPath)) {
|
|
7549
|
+
return [{
|
|
7550
|
+
path: manifestPath,
|
|
7551
|
+
message: `${SCAFFOLD_CONFIG_PATH} exists but ${ADMIN_AI_MANIFEST_FILENAME} is missing`,
|
|
7552
|
+
action: "regenerate the platform files so AI tooling has a stable entrypoint"
|
|
7553
|
+
}];
|
|
7554
|
+
}
|
|
7555
|
+
const { manifest, issue } = parseManifest(manifestPath);
|
|
7556
|
+
if (issue !== undefined)
|
|
7557
|
+
return [issue];
|
|
7558
|
+
if (manifest === undefined)
|
|
7559
|
+
return [];
|
|
7560
|
+
const issues = [];
|
|
7561
|
+
if (!existsSync(join(projectDirectory, ADMIN_SCHEMA_FILENAME))) {
|
|
7562
|
+
issues.push({
|
|
7563
|
+
path: join(projectDirectory, ADMIN_SCHEMA_FILENAME),
|
|
7564
|
+
message: `${ADMIN_AI_MANIFEST_FILENAME} is present but ${ADMIN_SCHEMA_FILENAME} is missing`,
|
|
7565
|
+
action: "regenerate the platform files so editors can validate the manifest"
|
|
7566
|
+
});
|
|
7567
|
+
}
|
|
7568
|
+
const dependencies = declaredDependencies(project);
|
|
7569
|
+
const providerPackages = [
|
|
7570
|
+
manifest.providers.data.package,
|
|
7571
|
+
manifest.providers.auth.package
|
|
7572
|
+
].filter((name) => name !== null);
|
|
7573
|
+
const declaredCore = project.dependencies?.["@svadmin/core"] ?? project.devDependencies?.["@svadmin/core"];
|
|
7574
|
+
for (const packageName of new Set(providerPackages)) {
|
|
7575
|
+
if (!dependencies.has(packageName)) {
|
|
7576
|
+
issues.push({
|
|
7577
|
+
path: manifestPath,
|
|
7578
|
+
message: `svadmin.ai.json declares provider "${packageName}" but package.json does not depend on it`,
|
|
7579
|
+
action: `add ${packageName} to package.json or update svadmin.ai.json`
|
|
7580
|
+
});
|
|
7581
|
+
continue;
|
|
7582
|
+
}
|
|
7583
|
+
const meta = readInstalledProviderMeta(projectDirectory, packageName);
|
|
7584
|
+
if (meta === null)
|
|
7585
|
+
continue;
|
|
7586
|
+
if (meta.capabilities !== undefined && !Array.isArray(meta.capabilities)) {
|
|
7587
|
+
issues.push({
|
|
7588
|
+
path: manifestPath,
|
|
7589
|
+
message: `${packageName} declares a non-array svadmin.capabilities`,
|
|
7590
|
+
action: "fix the provider package metadata"
|
|
7591
|
+
});
|
|
7592
|
+
}
|
|
7593
|
+
if (typeof meta.core === "string" && declaredCore !== undefined && !coreRangesCompatible(declaredCore, meta.core)) {
|
|
7594
|
+
issues.push({
|
|
7595
|
+
path: manifestPath,
|
|
7596
|
+
message: `${packageName} declares @svadmin/core ${meta.core}, incompatible with the project range ${declaredCore}`,
|
|
7597
|
+
action: `align @svadmin/core with ${meta.core} or upgrade ${packageName}`
|
|
7598
|
+
});
|
|
7599
|
+
}
|
|
7600
|
+
}
|
|
7601
|
+
return issues;
|
|
7602
|
+
}
|
|
7603
|
+
|
|
6901
7604
|
// src/project-maintenance.ts
|
|
6902
7605
|
import {
|
|
6903
7606
|
constants,
|
|
6904
7607
|
copyFileSync,
|
|
6905
|
-
readFileSync as
|
|
7608
|
+
readFileSync as readFileSync3,
|
|
6906
7609
|
renameSync,
|
|
6907
7610
|
unlinkSync,
|
|
6908
7611
|
writeFileSync
|
|
@@ -6925,7 +7628,7 @@ function parseMaintainedPackageJson(packageJsonCandidate) {
|
|
|
6925
7628
|
return packageJson;
|
|
6926
7629
|
}
|
|
6927
7630
|
function readMaintainedPackageJson(packagePath) {
|
|
6928
|
-
const parsed = JSON.parse(
|
|
7631
|
+
const parsed = JSON.parse(readFileSync3(packagePath, "utf8"));
|
|
6929
7632
|
return parseMaintainedPackageJson(parsed);
|
|
6930
7633
|
}
|
|
6931
7634
|
function allProjectDependencies(project) {
|
|
@@ -7186,17 +7889,269 @@ function writeProjectPackageJsonUpgrade(packagePath, scaffold, backupDate) {
|
|
|
7186
7889
|
return { wrote: true, backupPath, plan: plannedUpgrade.plan };
|
|
7187
7890
|
}
|
|
7188
7891
|
|
|
7892
|
+
// src/add-command.ts
|
|
7893
|
+
var RESOURCE_NAME_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
7894
|
+
function parseAddArguments(args) {
|
|
7895
|
+
const [kind, ...rest] = args;
|
|
7896
|
+
if (kind !== "resource" && kind !== "provider" && kind !== "auth") {
|
|
7897
|
+
throw new Error("Usage: create-svadmin add <resource|provider|auth> <name> [--project-dir <dir>] [--write]");
|
|
7898
|
+
}
|
|
7899
|
+
let projectDirectory = process.cwd();
|
|
7900
|
+
let write = false;
|
|
7901
|
+
const positional = [];
|
|
7902
|
+
for (let index = 0;index < rest.length; index++) {
|
|
7903
|
+
const argument = rest[index];
|
|
7904
|
+
if (argument === "--write") {
|
|
7905
|
+
write = true;
|
|
7906
|
+
} else if (argument === "--project-dir") {
|
|
7907
|
+
const value = rest[index + 1];
|
|
7908
|
+
if (value === undefined)
|
|
7909
|
+
throw new Error("--project-dir requires a value");
|
|
7910
|
+
projectDirectory = path4.resolve(process.cwd(), value);
|
|
7911
|
+
index++;
|
|
7912
|
+
} else if (argument === undefined) {
|
|
7913
|
+
continue;
|
|
7914
|
+
} else if (argument.startsWith("-")) {
|
|
7915
|
+
throw new Error(`Unknown option: ${argument}`);
|
|
7916
|
+
} else {
|
|
7917
|
+
positional.push(argument);
|
|
7918
|
+
}
|
|
7919
|
+
}
|
|
7920
|
+
if (positional.length !== 1) {
|
|
7921
|
+
throw new Error(`Expected exactly one target, received: ${positional.join(", ") || "(none)"}`);
|
|
7922
|
+
}
|
|
7923
|
+
return { kind, target: positional[0], projectDirectory, write };
|
|
7924
|
+
}
|
|
7925
|
+
function humanize4(name) {
|
|
7926
|
+
return name.split("_").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
7927
|
+
}
|
|
7928
|
+
function assertProject(projectDirectory) {
|
|
7929
|
+
if (!fs4.existsSync(path4.join(projectDirectory, "package.json"))) {
|
|
7930
|
+
throw new Error(`Not a Node project: ${path4.join(projectDirectory, "package.json")} is missing`);
|
|
7931
|
+
}
|
|
7932
|
+
}
|
|
7933
|
+
function resourceFileContents(name) {
|
|
7934
|
+
const label = humanize4(name);
|
|
7935
|
+
return {
|
|
7936
|
+
index: `export { ${name}Resource, ${name}Definition } from './${name}.resource';
|
|
7937
|
+
`,
|
|
7938
|
+
resource: `import { Type } from '@sinclair/typebox';
|
|
7939
|
+
import { defineResource, type AdminResourceDefinition } from '@svadmin/core';
|
|
7940
|
+
|
|
7941
|
+
/**
|
|
7942
|
+
* ${label} runtime contract.
|
|
7943
|
+
* Extend the create/update schemas before enabling writes; empty object schemas
|
|
7944
|
+
* keep writes disabled until fields are declared.
|
|
7945
|
+
*/
|
|
7946
|
+
export const ${name}Resource = defineResource('${name}', {
|
|
7947
|
+
record: Type.Object({
|
|
7948
|
+
id: Type.String(),
|
|
7949
|
+
}),
|
|
7950
|
+
create: Type.Object({}),
|
|
7951
|
+
update: Type.Object({}),
|
|
7952
|
+
});
|
|
7953
|
+
|
|
7954
|
+
/** Menu and field metadata consumed by the resource registry. */
|
|
7955
|
+
export const ${name}Definition: AdminResourceDefinition = {
|
|
7956
|
+
name: '${name}',
|
|
7957
|
+
label: '${label}',
|
|
7958
|
+
icon: 'file',
|
|
7959
|
+
fields: [
|
|
7960
|
+
{ key: 'id', label: 'ID', type: 'text', showInForm: false },
|
|
7961
|
+
],
|
|
7962
|
+
contract: ${name}Resource,
|
|
7963
|
+
};
|
|
7964
|
+
`
|
|
7965
|
+
};
|
|
7966
|
+
}
|
|
7967
|
+
function planAddResource(projectDirectory, resourceName) {
|
|
7968
|
+
assertProject(projectDirectory);
|
|
7969
|
+
if (!RESOURCE_NAME_PATTERN.test(resourceName)) {
|
|
7970
|
+
throw new Error(`Invalid resource name "${resourceName}"; use lowercase letters, digits, and underscores (for example "order_items")`);
|
|
7971
|
+
}
|
|
7972
|
+
const files = resourceFileContents(resourceName);
|
|
7973
|
+
const base = path4.posix.join("src", "features", resourceName);
|
|
7974
|
+
const contents = {
|
|
7975
|
+
[`${base}/index.ts`]: files.index,
|
|
7976
|
+
[`${base}/${resourceName}.resource.ts`]: files.resource
|
|
7977
|
+
};
|
|
7978
|
+
const entries = Object.entries(contents).map(([relativePath, content]) => ({
|
|
7979
|
+
relativePath,
|
|
7980
|
+
content,
|
|
7981
|
+
filePath: path4.join(projectDirectory, relativePath),
|
|
7982
|
+
exists: fs4.existsSync(path4.join(projectDirectory, relativePath))
|
|
7983
|
+
}));
|
|
7984
|
+
return {
|
|
7985
|
+
projectDirectory,
|
|
7986
|
+
resourceName,
|
|
7987
|
+
entries,
|
|
7988
|
+
registrationHint: `Register it in src/resources.ts:
|
|
7989
|
+
import { ${resourceName}Definition } from './features/${resourceName}';
|
|
7990
|
+
// then add ${resourceName}Definition to the resources array`
|
|
7991
|
+
};
|
|
7992
|
+
}
|
|
7993
|
+
function writeAddResource(plan) {
|
|
7994
|
+
const written = [];
|
|
7995
|
+
const preserved = [];
|
|
7996
|
+
for (const entry of plan.entries) {
|
|
7997
|
+
if (entry.exists || fs4.existsSync(entry.filePath)) {
|
|
7998
|
+
preserved.push(entry.relativePath);
|
|
7999
|
+
continue;
|
|
8000
|
+
}
|
|
8001
|
+
fs4.mkdirSync(path4.dirname(entry.filePath), { recursive: true });
|
|
8002
|
+
fs4.writeFileSync(entry.filePath, entry.content);
|
|
8003
|
+
written.push(entry.relativePath);
|
|
8004
|
+
}
|
|
8005
|
+
return { written, preserved };
|
|
8006
|
+
}
|
|
8007
|
+
function providerPackages(scaffold, kind, choice) {
|
|
8008
|
+
const packs = kind === "provider" ? scaffold.svadmin.dataProviders[choice] : scaffold.svadmin.authProviders[choice];
|
|
8009
|
+
const packages = new Map;
|
|
8010
|
+
for (const packName of packs) {
|
|
8011
|
+
const pack = scaffold.svadmin.dependencyPacks[packName];
|
|
8012
|
+
if (pack === undefined)
|
|
8013
|
+
throw new Error(`Unknown dependency pack: ${packName}`);
|
|
8014
|
+
for (const [packageName, version] of Object.entries(pack))
|
|
8015
|
+
packages.set(packageName, version);
|
|
8016
|
+
}
|
|
8017
|
+
return [...packages].map(([packageName, version]) => ({ packageName, version }));
|
|
8018
|
+
}
|
|
8019
|
+
function readManifest(manifestPath) {
|
|
8020
|
+
if (!fs4.existsSync(manifestPath))
|
|
8021
|
+
return null;
|
|
8022
|
+
try {
|
|
8023
|
+
const parsed = JSON.parse(fs4.readFileSync(manifestPath, "utf8"));
|
|
8024
|
+
if (typeof parsed !== "object" || parsed === null || Reflect.get(parsed, "version") !== 1)
|
|
8025
|
+
return null;
|
|
8026
|
+
return parsed;
|
|
8027
|
+
} catch {
|
|
8028
|
+
return null;
|
|
8029
|
+
}
|
|
8030
|
+
}
|
|
8031
|
+
function planAddProvider(projectDirectory, scaffold, kind, choice) {
|
|
8032
|
+
assertProject(projectDirectory);
|
|
8033
|
+
const validChoices = kind === "provider" ? DATA_PROVIDER_CHOICES : AUTH_PROVIDER_CHOICES;
|
|
8034
|
+
if (!validChoices.includes(choice)) {
|
|
8035
|
+
throw new Error(`Unknown ${kind} "${choice}"; expected one of: ${validChoices.join(", ")}`);
|
|
8036
|
+
}
|
|
8037
|
+
const packageJsonPath = path4.join(projectDirectory, "package.json");
|
|
8038
|
+
const project = readMaintainedPackageJson(packageJsonPath);
|
|
8039
|
+
const dependencies = { ...project.dependencies ?? {} };
|
|
8040
|
+
const devDependencies = { ...project.devDependencies ?? {} };
|
|
8041
|
+
const addedDependencies = [];
|
|
8042
|
+
for (const dependency of providerPackages(scaffold, kind, choice)) {
|
|
8043
|
+
if (dependencies[dependency.packageName] === undefined && devDependencies[dependency.packageName] === undefined) {
|
|
8044
|
+
dependencies[dependency.packageName] = dependency.version;
|
|
8045
|
+
addedDependencies.push(dependency);
|
|
8046
|
+
}
|
|
8047
|
+
}
|
|
8048
|
+
const updatedPackageJson = { ...project, dependencies, devDependencies };
|
|
8049
|
+
const manifestPath = path4.join(projectDirectory, ADMIN_AI_MANIFEST_FILENAME);
|
|
8050
|
+
const manifest = readManifest(manifestPath);
|
|
8051
|
+
const descriptor = kind === "provider" ? SCAFFOLD_DATA_PROVIDERS[choice] : SCAFFOLD_AUTH_PROVIDERS[choice];
|
|
8052
|
+
const updatedManifest = manifest === null ? null : kind === "provider" ? { ...manifest, providers: { ...manifest.providers, data: { choice, ...descriptor } } } : { ...manifest, providers: { ...manifest.providers, auth: { choice, ...descriptor } } };
|
|
8053
|
+
return {
|
|
8054
|
+
projectDirectory,
|
|
8055
|
+
kind,
|
|
8056
|
+
choice,
|
|
8057
|
+
packageJsonPath,
|
|
8058
|
+
updatedPackageJson: addedDependencies.length === 0 ? null : updatedPackageJson,
|
|
8059
|
+
addedDependencies,
|
|
8060
|
+
manifestPath,
|
|
8061
|
+
updatedManifest: manifest === null ? null : updatedManifest
|
|
8062
|
+
};
|
|
8063
|
+
}
|
|
8064
|
+
function writeAddProvider(plan) {
|
|
8065
|
+
const written = [];
|
|
8066
|
+
const preserved = [];
|
|
8067
|
+
if (plan.updatedPackageJson !== null) {
|
|
8068
|
+
fs4.writeFileSync(plan.packageJsonPath, `${JSON.stringify(plan.updatedPackageJson, null, 2)}
|
|
8069
|
+
`);
|
|
8070
|
+
written.push("package.json");
|
|
8071
|
+
} else {
|
|
8072
|
+
preserved.push("package.json");
|
|
8073
|
+
}
|
|
8074
|
+
if (plan.updatedManifest !== null && fs4.existsSync(plan.manifestPath)) {
|
|
8075
|
+
fs4.writeFileSync(plan.manifestPath, `${JSON.stringify(plan.updatedManifest, null, 2)}
|
|
8076
|
+
`);
|
|
8077
|
+
written.push(ADMIN_AI_MANIFEST_FILENAME);
|
|
8078
|
+
}
|
|
8079
|
+
return { written, preserved };
|
|
8080
|
+
}
|
|
8081
|
+
function printAddResult(wrote, result, hint) {
|
|
8082
|
+
if (wrote) {
|
|
8083
|
+
if (result.written.length > 0) {
|
|
8084
|
+
console.log(import_picocolors3.default.green(` ✔ wrote ${result.written.join(", ")}`));
|
|
8085
|
+
}
|
|
8086
|
+
if (result.preserved.length > 0) {
|
|
8087
|
+
console.log(import_picocolors3.default.dim(` • preserved ${result.preserved.join(", ")}`));
|
|
8088
|
+
}
|
|
8089
|
+
}
|
|
8090
|
+
if (hint !== undefined)
|
|
8091
|
+
console.log(`
|
|
8092
|
+
${hint}`);
|
|
8093
|
+
console.log();
|
|
8094
|
+
}
|
|
8095
|
+
function addCommand(args, scaffold) {
|
|
8096
|
+
const options = parseAddArguments(args);
|
|
8097
|
+
console.log(`
|
|
8098
|
+
svadmin add ${options.kind} ${options.target} — ${options.projectDirectory}`);
|
|
8099
|
+
if (options.kind === "resource") {
|
|
8100
|
+
const plan = planAddResource(options.projectDirectory, options.target);
|
|
8101
|
+
for (const entry of plan.entries) {
|
|
8102
|
+
console.log(` ${entry.exists ? "preserve" : "add"} ${entry.relativePath}`);
|
|
8103
|
+
}
|
|
8104
|
+
if (!options.write) {
|
|
8105
|
+
console.log(`
|
|
8106
|
+
Dry run only; re-run with --write to create missing files.`);
|
|
8107
|
+
printAddResult(false, { written: [], preserved: [] }, plan.registrationHint);
|
|
8108
|
+
return;
|
|
8109
|
+
}
|
|
8110
|
+
printAddResult(true, writeAddResource(plan), plan.registrationHint);
|
|
8111
|
+
return;
|
|
8112
|
+
}
|
|
8113
|
+
const plan = planAddProvider(options.projectDirectory, scaffold, options.kind, options.target);
|
|
8114
|
+
if (plan.addedDependencies.length === 0) {
|
|
8115
|
+
console.log(import_picocolors3.default.green(" ✔ dependencies already present; nothing to add."));
|
|
8116
|
+
} else {
|
|
8117
|
+
for (const dependency of plan.addedDependencies) {
|
|
8118
|
+
console.log(` add ${dependency.packageName}@${dependency.version}`);
|
|
8119
|
+
}
|
|
8120
|
+
}
|
|
8121
|
+
if (plan.updatedManifest !== null) {
|
|
8122
|
+
console.log(` update ${ADMIN_AI_MANIFEST_FILENAME} -> ${options.kind}: ${options.target}`);
|
|
8123
|
+
}
|
|
8124
|
+
if (!options.write) {
|
|
8125
|
+
console.log(`
|
|
8126
|
+
Dry run only; re-run with --write to update package.json and the AI manifest.`);
|
|
8127
|
+
console.log(import_picocolors3.default.dim(" Update src/svadmin.config.ts to wire the new provider."));
|
|
8128
|
+
console.log();
|
|
8129
|
+
return;
|
|
8130
|
+
}
|
|
8131
|
+
printAddResult(true, writeAddProvider(plan), "Update src/svadmin.config.ts to wire the new provider.");
|
|
8132
|
+
}
|
|
8133
|
+
|
|
7189
8134
|
// src/index.ts
|
|
7190
8135
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
7191
|
-
var __dirname2 =
|
|
8136
|
+
var __dirname2 = path5.dirname(__filename2);
|
|
7192
8137
|
function loadShippedScaffoldManifest() {
|
|
7193
|
-
return loadScaffoldManifest(
|
|
8138
|
+
return loadScaffoldManifest(path5.join(__dirname2, "..", "scaffold-manifest.json"));
|
|
8139
|
+
}
|
|
8140
|
+
function shippedCliVersion() {
|
|
8141
|
+
try {
|
|
8142
|
+
const raw = fs5.readFileSync(path5.join(__dirname2, "..", "package.json"), "utf8");
|
|
8143
|
+
const parsed = JSON.parse(raw);
|
|
8144
|
+
const version = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "version") : undefined;
|
|
8145
|
+
return typeof version === "string" ? version : "0.0.0";
|
|
8146
|
+
} catch {
|
|
8147
|
+
return "0.0.0";
|
|
8148
|
+
}
|
|
7194
8149
|
}
|
|
7195
8150
|
function projectDirectoryFromArguments(positional) {
|
|
7196
8151
|
if (positional.length > 1) {
|
|
7197
8152
|
throw new Error(`Expected at most one project directory, received: ${positional.join(", ")}`);
|
|
7198
8153
|
}
|
|
7199
|
-
return
|
|
8154
|
+
return path5.resolve(process.cwd(), positional[0] ?? ".");
|
|
7200
8155
|
}
|
|
7201
8156
|
function doctorProjectDirectory(args) {
|
|
7202
8157
|
const unknownOption = args.find((argument) => argument.startsWith("-"));
|
|
@@ -7232,52 +8187,61 @@ function upgradeChangeMessage(change) {
|
|
|
7232
8187
|
return `update ${change.packageName} from ${change.from ?? "missing"} to ${change.to}`;
|
|
7233
8188
|
}
|
|
7234
8189
|
function printDoctorIssue(issue) {
|
|
7235
|
-
const marker = issue.kind === "drift" || issue.kind === "section" ?
|
|
8190
|
+
const marker = issue.kind === "drift" || issue.kind === "section" ? import_picocolors4.default.yellow(" ⚠") : import_picocolors4.default.red(" ✗");
|
|
7236
8191
|
console.log(`${marker} ${doctorIssueMessage(issue)}`);
|
|
7237
|
-
console.log(
|
|
8192
|
+
console.log(import_picocolors4.default.dim(` → ${issue.action}`));
|
|
7238
8193
|
}
|
|
7239
8194
|
function printDoctorReport(report, projectDirectory) {
|
|
7240
8195
|
console.log();
|
|
7241
|
-
console.log(
|
|
8196
|
+
console.log(import_picocolors4.default.bold(`svadmin doctor — ${projectDirectory}`));
|
|
7242
8197
|
if (report.status === "clean") {
|
|
7243
|
-
console.log(
|
|
8198
|
+
console.log(import_picocolors4.default.green(" ✔ Dependencies match the shipped svadmin scaffold."));
|
|
7244
8199
|
} else {
|
|
7245
8200
|
for (const issue of report.issues)
|
|
7246
8201
|
printDoctorIssue(issue);
|
|
7247
8202
|
console.log();
|
|
7248
|
-
console.log(
|
|
8203
|
+
console.log(import_picocolors4.default.yellow(` ${report.issues.length} actionable issue(s) found.`));
|
|
7249
8204
|
}
|
|
7250
8205
|
console.log();
|
|
7251
8206
|
}
|
|
7252
8207
|
function doctor(args) {
|
|
7253
8208
|
const projectDirectory = doctorProjectDirectory(args);
|
|
7254
|
-
const project = readMaintainedPackageJson(
|
|
8209
|
+
const project = readMaintainedPackageJson(path5.join(projectDirectory, "package.json"));
|
|
7255
8210
|
const report = doctorProjectPackageJson(project, loadShippedScaffoldManifest());
|
|
7256
8211
|
printDoctorReport(report, projectDirectory);
|
|
7257
|
-
|
|
8212
|
+
const manifestIssues = checkAdminManifest(projectDirectory, project);
|
|
8213
|
+
if (manifestIssues.length > 0) {
|
|
8214
|
+
console.log(import_picocolors4.default.bold(" Platform entrypoints:"));
|
|
8215
|
+
for (const issue of manifestIssues) {
|
|
8216
|
+
console.log(import_picocolors4.default.yellow(` ⚠ ${issue.message}`));
|
|
8217
|
+
console.log(import_picocolors4.default.dim(` → ${issue.action}`));
|
|
8218
|
+
}
|
|
8219
|
+
console.log();
|
|
8220
|
+
}
|
|
8221
|
+
process.exitCode = report.exitCode === 0 && manifestIssues.length === 0 ? 0 : 1;
|
|
7258
8222
|
}
|
|
7259
8223
|
function printUpgradeChanges(upgradeExecution) {
|
|
7260
8224
|
for (const change of upgradeExecution.plan.changes) {
|
|
7261
|
-
console.log(` ${
|
|
8225
|
+
console.log(` ${import_picocolors4.default.cyan("•")} ${upgradeChangeMessage(change)}`);
|
|
7262
8226
|
}
|
|
7263
8227
|
console.log();
|
|
7264
8228
|
}
|
|
7265
8229
|
function printUpgradeOutcome(upgradeExecution, packagePath) {
|
|
7266
8230
|
if (upgradeExecution.wrote) {
|
|
7267
|
-
console.log(
|
|
7268
|
-
console.log(` Backup: ${
|
|
7269
|
-
console.log(` Restore by copying the backup over: ${
|
|
8231
|
+
console.log(import_picocolors4.default.green(" ✔ package.json updated."));
|
|
8232
|
+
console.log(` Backup: ${import_picocolors4.default.cyan(upgradeExecution.backupPath)}`);
|
|
8233
|
+
console.log(` Restore by copying the backup over: ${import_picocolors4.default.cyan(packagePath)}`);
|
|
7270
8234
|
} else {
|
|
7271
|
-
console.log(
|
|
8235
|
+
console.log(import_picocolors4.default.yellow(" Dry run only; package.json was not changed."));
|
|
7272
8236
|
console.log(" Re-run this command with --write to apply the plan.");
|
|
7273
8237
|
}
|
|
7274
8238
|
console.log();
|
|
7275
8239
|
}
|
|
7276
8240
|
function printUpgradeExecution(upgradeExecution, projectDirectory, packagePath) {
|
|
7277
8241
|
console.log();
|
|
7278
|
-
console.log(
|
|
8242
|
+
console.log(import_picocolors4.default.bold(`svadmin upgrade — ${projectDirectory}`));
|
|
7279
8243
|
if (upgradeExecution.plan.changes.length === 0) {
|
|
7280
|
-
console.log(
|
|
8244
|
+
console.log(import_picocolors4.default.green(" ✔ package.json already matches the shipped scaffold."));
|
|
7281
8245
|
console.log();
|
|
7282
8246
|
return;
|
|
7283
8247
|
}
|
|
@@ -7286,61 +8250,64 @@ function printUpgradeExecution(upgradeExecution, projectDirectory, packagePath)
|
|
|
7286
8250
|
}
|
|
7287
8251
|
function upgrade(args) {
|
|
7288
8252
|
const commandArguments = parseUpgradeArguments(args);
|
|
7289
|
-
const packagePath =
|
|
8253
|
+
const packagePath = path5.join(commandArguments.projectDirectory, "package.json");
|
|
7290
8254
|
const scaffoldManifest = loadShippedScaffoldManifest();
|
|
7291
8255
|
const upgradeExecution = commandArguments.write ? writeProjectPackageJsonUpgrade(packagePath, scaffoldManifest, new Date) : planProjectPackageFileUpgrade(packagePath, scaffoldManifest);
|
|
7292
8256
|
printUpgradeExecution(upgradeExecution, commandArguments.projectDirectory, packagePath);
|
|
7293
8257
|
}
|
|
7294
8258
|
var GUIDANCE_FILES = ["DESIGN.md", "AGENTS.md"];
|
|
7295
8259
|
function missingGuidanceFiles(projectDirectory) {
|
|
7296
|
-
return GUIDANCE_FILES.filter((fileName) => !
|
|
8260
|
+
return GUIDANCE_FILES.filter((fileName) => !fs5.existsSync(path5.join(projectDirectory, fileName)));
|
|
7297
8261
|
}
|
|
7298
8262
|
function printGuidancePlan(projectDirectory, missingFiles) {
|
|
7299
8263
|
console.log();
|
|
7300
|
-
console.log(
|
|
8264
|
+
console.log(import_picocolors4.default.bold(`svadmin guidance — ${projectDirectory}`));
|
|
7301
8265
|
for (const fileName of missingFiles) {
|
|
7302
|
-
console.log(` ${
|
|
8266
|
+
console.log(` ${import_picocolors4.default.cyan("•")} add ${fileName}`);
|
|
7303
8267
|
}
|
|
7304
8268
|
console.log();
|
|
7305
8269
|
}
|
|
7306
8270
|
function installMissingGuidanceFiles(guidanceDirectory, projectDirectory, missingFiles) {
|
|
7307
8271
|
for (const fileName of missingFiles) {
|
|
7308
|
-
|
|
8272
|
+
fs5.copyFileSync(path5.join(guidanceDirectory, fileName), path5.join(projectDirectory, fileName));
|
|
7309
8273
|
}
|
|
7310
8274
|
}
|
|
7311
8275
|
function guidance(args) {
|
|
7312
8276
|
const { projectDirectory, write } = parseUpgradeArguments(args);
|
|
7313
|
-
const guidanceDirectory =
|
|
7314
|
-
if (!
|
|
8277
|
+
const guidanceDirectory = path5.join(__dirname2, "..", "guidance");
|
|
8278
|
+
if (!fs5.existsSync(projectDirectory))
|
|
7315
8279
|
throw new Error(`Project directory does not exist: ${projectDirectory}`);
|
|
7316
|
-
if (!
|
|
8280
|
+
if (!fs5.existsSync(guidanceDirectory))
|
|
7317
8281
|
throw new Error("Shipped svadmin guidance files are missing");
|
|
7318
8282
|
const missingFiles = missingGuidanceFiles(projectDirectory);
|
|
7319
8283
|
if (missingFiles.length === 0) {
|
|
7320
|
-
console.log(
|
|
8284
|
+
console.log(import_picocolors4.default.green(`
|
|
7321
8285
|
✔ DESIGN.md and AGENTS.md already exist; nothing was changed.
|
|
7322
8286
|
`));
|
|
7323
8287
|
return;
|
|
7324
8288
|
}
|
|
7325
8289
|
printGuidancePlan(projectDirectory, missingFiles);
|
|
7326
8290
|
if (!write) {
|
|
7327
|
-
console.log(
|
|
8291
|
+
console.log(import_picocolors4.default.yellow(` Dry run only; re-run with --write to add missing guidance files.
|
|
7328
8292
|
`));
|
|
7329
8293
|
return;
|
|
7330
8294
|
}
|
|
7331
8295
|
installMissingGuidanceFiles(guidanceDirectory, projectDirectory, missingFiles);
|
|
7332
|
-
console.log(
|
|
8296
|
+
console.log(import_picocolors4.default.green(` ✔ Added ${missingFiles.length} guidance file(s); existing files were preserved.`));
|
|
7333
8297
|
console.log();
|
|
7334
8298
|
}
|
|
7335
|
-
async function init() {
|
|
8299
|
+
async function init(args) {
|
|
7336
8300
|
console.log();
|
|
7337
|
-
console.log(
|
|
7338
|
-
console.log(
|
|
7339
|
-
console.log(
|
|
7340
|
-
console.log(
|
|
8301
|
+
console.log(import_picocolors4.default.cyan(" ╔═══════════════════════════════════╗"));
|
|
8302
|
+
console.log(import_picocolors4.default.cyan(" ║ ") + import_picocolors4.default.bold("create-svadmin") + import_picocolors4.default.cyan(" ║"));
|
|
8303
|
+
console.log(import_picocolors4.default.cyan(" ║ ") + import_picocolors4.default.dim("Headless Admin for Svelte 5") + import_picocolors4.default.cyan(" ║"));
|
|
8304
|
+
console.log(import_picocolors4.default.cyan(" ╚═══════════════════════════════════╝"));
|
|
7341
8305
|
console.log();
|
|
7342
|
-
const
|
|
7343
|
-
|
|
8306
|
+
const parsed = parseInitArguments(args);
|
|
8307
|
+
const presetSelections = resolvePresetSelections(parsed);
|
|
8308
|
+
const questions = [];
|
|
8309
|
+
if (parsed.projectName === undefined) {
|
|
8310
|
+
questions.push({
|
|
7344
8311
|
type: "text",
|
|
7345
8312
|
name: "projectName",
|
|
7346
8313
|
message: "Project name:",
|
|
@@ -7348,99 +8315,130 @@ async function init() {
|
|
|
7348
8315
|
validate: (value) => {
|
|
7349
8316
|
if (!value.trim())
|
|
7350
8317
|
return "Project name is required";
|
|
7351
|
-
if (
|
|
8318
|
+
if (fs5.existsSync(value.trim()) && fs5.readdirSync(value.trim()).length > 0) {
|
|
7352
8319
|
return "Directory already exists and is not empty";
|
|
7353
8320
|
}
|
|
7354
8321
|
return true;
|
|
7355
8322
|
}
|
|
7356
|
-
}
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
8323
|
+
});
|
|
8324
|
+
}
|
|
8325
|
+
if (presetSelections === undefined) {
|
|
8326
|
+
if (parsed.dataProvider === undefined) {
|
|
8327
|
+
questions.push({
|
|
8328
|
+
type: "select",
|
|
8329
|
+
name: "dataProvider",
|
|
8330
|
+
message: "Data Provider:",
|
|
8331
|
+
choices: [
|
|
8332
|
+
{ title: "Simple REST", value: "simple-rest", description: "Standard JSON APIs / JSON Server" },
|
|
8333
|
+
{ title: "Supabase", value: "supabase", description: "PostgreSQL Backend-as-a-Service" },
|
|
8334
|
+
{ title: "GraphQL", value: "graphql", description: "Generic GraphQL endpoints" },
|
|
8335
|
+
{ title: "Custom", value: "none", description: "Implement your own DataProvider" }
|
|
8336
|
+
],
|
|
8337
|
+
initial: 0
|
|
8338
|
+
});
|
|
8339
|
+
}
|
|
8340
|
+
if (parsed.authProvider === undefined) {
|
|
8341
|
+
questions.push({
|
|
8342
|
+
type: "select",
|
|
8343
|
+
name: "authProvider",
|
|
8344
|
+
message: "Auth Provider:",
|
|
8345
|
+
choices: [
|
|
8346
|
+
{ title: "Mock (Demo)", value: "mock", description: "Built-in mock for development" },
|
|
8347
|
+
{ title: "Simple REST JWT", value: "jwt", description: "JWT-based auth via REST API" },
|
|
8348
|
+
{ title: "Supabase Auth", value: "supabase", description: "Supabase authentication" },
|
|
8349
|
+
{ title: "None", value: "none", description: "No authentication" }
|
|
8350
|
+
],
|
|
8351
|
+
initial: 0
|
|
8352
|
+
});
|
|
8353
|
+
}
|
|
8354
|
+
}
|
|
8355
|
+
if (parsed.installDependencies === undefined) {
|
|
8356
|
+
questions.push({
|
|
7382
8357
|
type: "confirm",
|
|
7383
8358
|
name: "installDeps",
|
|
7384
8359
|
message: "Install dependencies now?",
|
|
7385
8360
|
initial: true
|
|
7386
|
-
}
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
8361
|
+
});
|
|
8362
|
+
}
|
|
8363
|
+
const answers = questions.length > 0 ? await import_prompts2.default(questions) : {};
|
|
8364
|
+
const resolvedDataProvider = parsed.dataProvider ?? presetSelections?.dataProvider ?? answers.dataProvider;
|
|
8365
|
+
const resolvedAuthProvider = parsed.authProvider ?? presetSelections?.authProvider ?? answers.authProvider;
|
|
8366
|
+
const projectName = parsed.projectName ?? answers.projectName;
|
|
8367
|
+
if (!projectName || resolvedDataProvider === undefined || resolvedAuthProvider === undefined) {
|
|
8368
|
+
console.log(import_picocolors4.default.red(`
|
|
7390
8369
|
Operation cancelled.
|
|
7391
8370
|
`));
|
|
7392
8371
|
return;
|
|
7393
8372
|
}
|
|
7394
|
-
const
|
|
7395
|
-
|
|
7396
|
-
|
|
8373
|
+
const response = {
|
|
8374
|
+
projectName,
|
|
8375
|
+
dataProvider: resolvedDataProvider,
|
|
8376
|
+
authProvider: resolvedAuthProvider,
|
|
8377
|
+
installDeps: parsed.installDependencies ?? answers.installDeps ?? true
|
|
8378
|
+
};
|
|
8379
|
+
const projectDir = path5.resolve(process.cwd(), response.projectName.trim());
|
|
8380
|
+
if (!fs5.existsSync(projectDir)) {
|
|
8381
|
+
fs5.mkdirSync(projectDir, { recursive: true });
|
|
7397
8382
|
}
|
|
7398
8383
|
console.log(`
|
|
7399
|
-
${
|
|
8384
|
+
${import_picocolors4.default.bold("Scaffolding")} project in ${import_picocolors4.default.green(projectDir)}...
|
|
7400
8385
|
`);
|
|
7401
|
-
const templateDir =
|
|
7402
|
-
const guidanceDir =
|
|
8386
|
+
const templateDir = path5.join(__dirname2, "..", "template");
|
|
8387
|
+
const guidanceDir = path5.join(__dirname2, "..", "guidance");
|
|
7403
8388
|
const scaffoldManifest = loadShippedScaffoldManifest();
|
|
7404
8389
|
function copyDir(src, dest) {
|
|
7405
|
-
|
|
7406
|
-
const entries =
|
|
8390
|
+
fs5.mkdirSync(dest, { recursive: true });
|
|
8391
|
+
const entries = fs5.readdirSync(src, { withFileTypes: true });
|
|
7407
8392
|
for (const entry of entries) {
|
|
7408
|
-
const srcPath =
|
|
7409
|
-
const destPath =
|
|
8393
|
+
const srcPath = path5.join(src, entry.name);
|
|
8394
|
+
const destPath = path5.join(dest, entry.name === "_gitignore" ? ".gitignore" : entry.name);
|
|
7410
8395
|
if (entry.isDirectory()) {
|
|
7411
8396
|
copyDir(srcPath, destPath);
|
|
7412
8397
|
} else {
|
|
7413
|
-
|
|
8398
|
+
fs5.copyFileSync(srcPath, destPath);
|
|
7414
8399
|
}
|
|
7415
8400
|
}
|
|
7416
8401
|
}
|
|
7417
|
-
if (
|
|
8402
|
+
if (fs5.existsSync(templateDir)) {
|
|
7418
8403
|
copyDir(templateDir, projectDir);
|
|
7419
|
-
console.log(
|
|
8404
|
+
console.log(import_picocolors4.default.green(" ✔") + " Template files copied");
|
|
7420
8405
|
}
|
|
7421
|
-
if (
|
|
8406
|
+
if (fs5.existsSync(guidanceDir)) {
|
|
7422
8407
|
copyDir(guidanceDir, projectDir);
|
|
7423
|
-
console.log(
|
|
8408
|
+
console.log(import_picocolors4.default.green(" ✔") + " AI and design guidance copied");
|
|
7424
8409
|
}
|
|
8410
|
+
const platformFiles = buildScaffoldPlatformFiles({
|
|
8411
|
+
projectName: response.projectName.trim(),
|
|
8412
|
+
dataProvider: response.dataProvider,
|
|
8413
|
+
authProvider: response.authProvider,
|
|
8414
|
+
scaffoldVersion: shippedCliVersion(),
|
|
8415
|
+
...scaffoldManifest.dependencies["@svadmin/core"] === undefined ? {} : { coreVersionRange: scaffoldManifest.dependencies["@svadmin/core"] }
|
|
8416
|
+
});
|
|
8417
|
+
for (const file of [platformFiles.config, platformFiles.aiManifest, platformFiles.schema]) {
|
|
8418
|
+
const target = path5.join(projectDir, file.path);
|
|
8419
|
+
fs5.mkdirSync(path5.dirname(target), { recursive: true });
|
|
8420
|
+
fs5.writeFileSync(target, file.content);
|
|
8421
|
+
}
|
|
8422
|
+
console.log(import_picocolors4.default.green(" ✔") + " svadmin.config.ts, svadmin.ai.json and svadmin.schema.json generated");
|
|
7425
8423
|
const packageJson = createProjectPackageJson(scaffoldManifest, {
|
|
7426
8424
|
projectName: response.projectName,
|
|
7427
8425
|
dataProvider: response.dataProvider,
|
|
7428
8426
|
authProvider: response.authProvider
|
|
7429
8427
|
});
|
|
7430
|
-
|
|
8428
|
+
fs5.writeFileSync(path5.join(projectDir, "package.json"), `${JSON.stringify(packageJson, null, 2)}
|
|
7431
8429
|
`);
|
|
7432
|
-
console.log(
|
|
7433
|
-
|
|
8430
|
+
console.log(import_picocolors4.default.green(" ✔") + " package.json generated");
|
|
8431
|
+
fs5.writeFileSync(path5.join(projectDir, ".gitignore"), `node_modules
|
|
7434
8432
|
dist
|
|
7435
8433
|
.svelte-kit
|
|
7436
8434
|
.env
|
|
7437
8435
|
.env.local
|
|
7438
8436
|
*.local
|
|
7439
8437
|
`);
|
|
7440
|
-
console.log(
|
|
8438
|
+
console.log(import_picocolors4.default.green(" ✔") + " .gitignore generated");
|
|
7441
8439
|
const dpLabel = response.dataProvider === "simple-rest" ? "Simple REST" : response.dataProvider === "supabase" ? "Supabase" : response.dataProvider === "graphql" ? "GraphQL" : "Custom";
|
|
7442
8440
|
const authLabel = response.authProvider === "mock" ? "Mock (demo)" : response.authProvider === "jwt" ? "JWT" : response.authProvider === "supabase" ? "Supabase Auth" : "None";
|
|
7443
|
-
|
|
8441
|
+
fs5.writeFileSync(path5.join(projectDir, "README.md"), `# ${response.projectName}
|
|
7444
8442
|
|
|
7445
8443
|
Built with [svadmin](https://github.com/vibeunion/svadmin) — Headless Admin Framework for Svelte 5.
|
|
7446
8444
|
|
|
@@ -7458,30 +8456,30 @@ bun run dev
|
|
|
7458
8456
|
- **Auth**: ${authLabel}
|
|
7459
8457
|
- **State**: TanStack Query v6
|
|
7460
8458
|
`);
|
|
7461
|
-
console.log(
|
|
8459
|
+
console.log(import_picocolors4.default.green(" ✔") + " README.md generated");
|
|
7462
8460
|
if (response.installDeps) {
|
|
7463
8461
|
console.log(`
|
|
7464
|
-
${
|
|
8462
|
+
${import_picocolors4.default.bold("Installing dependencies...")}
|
|
7465
8463
|
`);
|
|
7466
8464
|
const bunInstall = spawnSync("bun", ["install"], { cwd: projectDir, stdio: "inherit" });
|
|
7467
8465
|
if (bunInstall.status !== 0) {
|
|
7468
8466
|
const npmInstall = spawnSync("npm", ["install"], { cwd: projectDir, stdio: "inherit" });
|
|
7469
8467
|
if (npmInstall.status !== 0) {
|
|
7470
|
-
console.log(
|
|
8468
|
+
console.log(import_picocolors4.default.yellow("\n ⚠ Auto-install failed. Run `bun install` or `npm install` manually."));
|
|
7471
8469
|
}
|
|
7472
8470
|
}
|
|
7473
8471
|
}
|
|
7474
8472
|
console.log();
|
|
7475
|
-
console.log(
|
|
8473
|
+
console.log(import_picocolors4.default.green(import_picocolors4.default.bold(" ✔ Project ready!")));
|
|
7476
8474
|
console.log();
|
|
7477
8475
|
console.log(" Next steps:");
|
|
7478
|
-
console.log(` ${
|
|
8476
|
+
console.log(` ${import_picocolors4.default.cyan(`cd ${response.projectName}`)}`);
|
|
7479
8477
|
if (!response.installDeps) {
|
|
7480
|
-
console.log(` ${
|
|
8478
|
+
console.log(` ${import_picocolors4.default.cyan("bun install")}`);
|
|
7481
8479
|
}
|
|
7482
|
-
console.log(` ${
|
|
8480
|
+
console.log(` ${import_picocolors4.default.cyan("bun run dev")}`);
|
|
7483
8481
|
console.log();
|
|
7484
|
-
console.log(` Docs: ${
|
|
8482
|
+
console.log(` Docs: ${import_picocolors4.default.blue("https://github.com/vibeunion/svadmin")}`);
|
|
7485
8483
|
console.log();
|
|
7486
8484
|
}
|
|
7487
8485
|
var EJECT_COMPONENTS = [
|
|
@@ -7510,64 +8508,64 @@ var EJECT_COMPONENTS = [
|
|
|
7510
8508
|
];
|
|
7511
8509
|
async function eject(args) {
|
|
7512
8510
|
console.log();
|
|
7513
|
-
console.log(
|
|
8511
|
+
console.log(import_picocolors4.default.cyan(" svadmin eject") + import_picocolors4.default.dim(" — copy internal components for deep customization"));
|
|
7514
8512
|
console.log();
|
|
7515
8513
|
const requested = args.filter((a) => !a.startsWith("-"));
|
|
7516
8514
|
const toEject = requested.length > 0 ? requested.filter((name) => {
|
|
7517
8515
|
if (!EJECT_COMPONENTS.includes(name)) {
|
|
7518
|
-
console.log(
|
|
8516
|
+
console.log(import_picocolors4.default.yellow(` ⚠ Unknown component: ${name} (skipped)`));
|
|
7519
8517
|
return false;
|
|
7520
8518
|
}
|
|
7521
8519
|
return true;
|
|
7522
8520
|
}) : [...EJECT_COMPONENTS];
|
|
7523
8521
|
if (toEject.length === 0) {
|
|
7524
|
-
console.log(
|
|
8522
|
+
console.log(import_picocolors4.default.red(" No valid components to eject."));
|
|
7525
8523
|
console.log(` Available: ${EJECT_COMPONENTS.join(", ")}`);
|
|
7526
8524
|
return;
|
|
7527
8525
|
}
|
|
7528
8526
|
let uiSrcDir;
|
|
7529
8527
|
try {
|
|
7530
8528
|
const require2 = createRequire2(import.meta.url);
|
|
7531
|
-
const uiPkg =
|
|
7532
|
-
uiSrcDir =
|
|
8529
|
+
const uiPkg = path5.dirname(require2.resolve("@svadmin/ui/package.json"));
|
|
8530
|
+
uiSrcDir = path5.join(uiPkg, "src", "components");
|
|
7533
8531
|
} catch {
|
|
7534
|
-
const nm =
|
|
7535
|
-
if (
|
|
8532
|
+
const nm = path5.join(process.cwd(), "node_modules", "@svadmin", "ui", "src", "components");
|
|
8533
|
+
if (fs5.existsSync(nm)) {
|
|
7536
8534
|
uiSrcDir = nm;
|
|
7537
8535
|
} else {
|
|
7538
|
-
console.log(
|
|
8536
|
+
console.log(import_picocolors4.default.red(" ✗ Cannot find @svadmin/ui. Run `bun install` first."));
|
|
7539
8537
|
return;
|
|
7540
8538
|
}
|
|
7541
8539
|
}
|
|
7542
|
-
const destDir =
|
|
7543
|
-
|
|
8540
|
+
const destDir = path5.join(process.cwd(), "src", "components", "svadmin");
|
|
8541
|
+
fs5.mkdirSync(destDir, { recursive: true });
|
|
7544
8542
|
let copied = 0;
|
|
7545
8543
|
for (const name of toEject) {
|
|
7546
|
-
const srcFile =
|
|
7547
|
-
const srcFileAlt =
|
|
7548
|
-
const src =
|
|
8544
|
+
const srcFile = path5.join(uiSrcDir, `${name}.svelte`);
|
|
8545
|
+
const srcFileAlt = path5.join(uiSrcDir, "fields", `${name}.svelte`);
|
|
8546
|
+
const src = fs5.existsSync(srcFile) ? srcFile : fs5.existsSync(srcFileAlt) ? srcFileAlt : null;
|
|
7549
8547
|
if (!src) {
|
|
7550
|
-
console.log(
|
|
8548
|
+
console.log(import_picocolors4.default.yellow(` ⚠ ${name}.svelte not found in @svadmin/ui (skipped)`));
|
|
7551
8549
|
continue;
|
|
7552
8550
|
}
|
|
7553
|
-
let content =
|
|
8551
|
+
let content = fs5.readFileSync(src, "utf-8");
|
|
7554
8552
|
content = content.replace(/from\s+['"]\.\/ui\//g, "from '@svadmin/ui/components/ui/");
|
|
7555
8553
|
content = content.replace(/from\s+['"]\.\/((?!ui\/)[^'"]+)['"]/g, "from './$1'");
|
|
7556
|
-
const destFile =
|
|
7557
|
-
|
|
7558
|
-
console.log(
|
|
8554
|
+
const destFile = path5.join(destDir, `${name}.svelte`);
|
|
8555
|
+
fs5.writeFileSync(destFile, content);
|
|
8556
|
+
console.log(import_picocolors4.default.green(" ✔") + ` ${name}.svelte → src/components/svadmin/`);
|
|
7559
8557
|
copied++;
|
|
7560
8558
|
}
|
|
7561
8559
|
console.log();
|
|
7562
8560
|
if (copied > 0) {
|
|
7563
|
-
console.log(
|
|
8561
|
+
console.log(import_picocolors4.default.green(import_picocolors4.default.bold(` ✔ Ejected ${copied} component(s)`)));
|
|
7564
8562
|
console.log();
|
|
7565
8563
|
console.log(" Usage: import overrides in your AdminApp and pass via `components` prop:");
|
|
7566
8564
|
console.log();
|
|
7567
|
-
console.log(
|
|
7568
|
-
console.log(
|
|
8565
|
+
console.log(import_picocolors4.default.dim(' import CustomLayout from "./components/svadmin/Layout.svelte";'));
|
|
8566
|
+
console.log(import_picocolors4.default.dim(" <AdminApp components={{ Layout: CustomLayout }} ... />"));
|
|
7569
8567
|
} else {
|
|
7570
|
-
console.log(
|
|
8568
|
+
console.log(import_picocolors4.default.yellow(" No components were ejected."));
|
|
7571
8569
|
}
|
|
7572
8570
|
console.log();
|
|
7573
8571
|
}
|
|
@@ -7575,7 +8573,7 @@ var [, , subcommand, ...rest] = process.argv;
|
|
|
7575
8573
|
var runCommand = (command) => {
|
|
7576
8574
|
Promise.resolve().then(command).catch((error) => {
|
|
7577
8575
|
const message = error instanceof Error ? error.message : String(error);
|
|
7578
|
-
console.error(
|
|
8576
|
+
console.error(import_picocolors4.default.red(`
|
|
7579
8577
|
✗ ${message}
|
|
7580
8578
|
`));
|
|
7581
8579
|
process.exitCode = 2;
|
|
@@ -7587,10 +8585,14 @@ if (subcommand === "eject") {
|
|
|
7587
8585
|
runCommand(() => doctor(rest));
|
|
7588
8586
|
} else if (subcommand === "upgrade") {
|
|
7589
8587
|
runCommand(() => upgrade(rest));
|
|
8588
|
+
} else if (subcommand === "migrate") {
|
|
8589
|
+
runCommand(() => upgrade(rest));
|
|
7590
8590
|
} else if (subcommand === "guidance") {
|
|
7591
8591
|
runCommand(() => guidance(rest));
|
|
7592
8592
|
} else if (subcommand === "infer") {
|
|
7593
8593
|
runCommand(() => inferCommand(rest));
|
|
8594
|
+
} else if (subcommand === "add") {
|
|
8595
|
+
runCommand(() => addCommand(rest, loadShippedScaffoldManifest()));
|
|
7594
8596
|
} else if (subcommand === "generate" || subcommand === "gen") {
|
|
7595
8597
|
runCommand(() => generateCommand(rest));
|
|
7596
8598
|
} else if (subcommand === "lite") {
|
|
@@ -7601,6 +8603,8 @@ if (subcommand === "eject") {
|
|
|
7601
8603
|
} else {
|
|
7602
8604
|
runCommand(() => liteInitCommand(rest.slice(1)));
|
|
7603
8605
|
}
|
|
8606
|
+
} else if (subcommand === "init") {
|
|
8607
|
+
runCommand(() => init(rest));
|
|
7604
8608
|
} else {
|
|
7605
|
-
runCommand(init);
|
|
8609
|
+
runCommand(() => init(subcommand === undefined ? rest : [subcommand, ...rest]));
|
|
7606
8610
|
}
|