@holo-js/cli 0.2.6 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/bin/holo.mjs +417 -109
  2. package/dist/{broadcast-JBQWQZSO.mjs → broadcast-PJLFVZH4.mjs} +12 -12
  3. package/dist/cache-KR4ACOYD.mjs +137 -0
  4. package/dist/{cache-migrations-77TLXIVG.mjs → cache-migrations-J34RE4WH.mjs} +13 -13
  5. package/dist/{chunk-LXGQCG56.mjs → chunk-2XXJ76YC.mjs} +1 -1
  6. package/dist/chunk-3ICVWL2M.mjs +491 -0
  7. package/dist/{chunk-YEFJBN56.mjs → chunk-4VYR2ARA.mjs} +11 -8
  8. package/dist/{chunk-VP2E62DF.mjs → chunk-7KZVFDSI.mjs} +49 -349
  9. package/dist/chunk-AZ6NVJAP.mjs +360 -0
  10. package/dist/chunk-CT23GSEC.mjs +156 -0
  11. package/dist/{chunk-7B2RVYLL.mjs → chunk-FFTJKBQF.mjs} +293 -399
  12. package/dist/{chunk-KS5TWO75.mjs → chunk-I3RRWDLV.mjs} +7 -7
  13. package/dist/{chunk-J76GH2DR.mjs → chunk-L3YATEQY.mjs} +129 -86
  14. package/dist/{chunk-4K4CWMMP.mjs → chunk-L6FF22IV.mjs} +76 -61
  15. package/dist/{chunk-ILU426CF.mjs → chunk-PVVGQSUP.mjs} +482 -2
  16. package/dist/chunk-XBOVM6LO.mjs +41 -0
  17. package/dist/{chunk-I7QBCEV7.mjs → chunk-Y355VYRN.mjs} +5 -3
  18. package/dist/{chunk-VRGB6DIS.mjs → chunk-YJWHIQ45.mjs} +2 -0
  19. package/dist/{config-MD27U4FM.mjs → config-XVPV6DJW.mjs} +3 -3
  20. package/dist/{dev-L4Y2GU2E.mjs → dev-NTQTEY5B.mjs} +9 -8
  21. package/dist/{discovery-GWTBF5RZ.mjs → discovery-TMPUQQHI.mjs} +8 -6
  22. package/dist/{generators-VCYISHWO.mjs → generators-3FDCCJDC.mjs} +16 -15
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.mjs +419 -111
  25. package/dist/{media-migrations-BNVAPYEM.mjs → media-migrations-UJ6BMUSZ.mjs} +12 -12
  26. package/dist/plugins-USHY6SPF.mjs +24 -0
  27. package/dist/{queue-5BXSDFCO.mjs → queue-6U7JU4T6.mjs} +53 -17
  28. package/dist/{queue-D4LSWNS2.mjs → queue-VJEFD6SE.mjs} +55 -19
  29. package/dist/{queue-migrations-EF4AMMSY.mjs → queue-migrations-6YUUOKMH.mjs} +17 -14
  30. package/dist/{runtime-RPMZMTD4.mjs → runtime-FVRB4O2M.mjs} +8 -8
  31. package/dist/{runtime-GSXF4NB3.mjs → runtime-P5R7H3B4.mjs} +1 -1
  32. package/dist/runtime-worker.mjs +7 -1
  33. package/dist/{scaffold-NJ5VH73M.mjs → scaffold-F3AZDNPG.mjs} +9 -4
  34. package/dist/{security-K5PZS3E3.mjs → security-X4274RHX.mjs} +8 -7
  35. package/package.json +35 -8
  36. package/dist/broadcast-YGJFCEPP.mjs +0 -204
  37. package/dist/cache-HUGRWOHQ.mjs +0 -66
  38. package/dist/cache-OSJ4PD52.mjs +0 -66
  39. package/dist/cache-migrations-FKAHRP24.mjs +0 -173
  40. package/dist/chunk-AJLRAC5M.mjs +0 -497
  41. package/dist/chunk-C624G3R2.mjs +0 -3306
  42. package/dist/chunk-D7O4SU6N.mjs +0 -2
  43. package/dist/chunk-FGQ2I2YH.mjs +0 -22
  44. package/dist/chunk-LBJAJLKU.mjs +0 -25
  45. package/dist/dev-YPBQBEOE.mjs +0 -44
  46. package/dist/generators-H4NTV4DB.mjs +0 -520
  47. package/dist/media-migrations-NNB3DAQR.mjs +0 -106
  48. package/dist/queue-migrations-LXEWXJYT.mjs +0 -168
  49. package/dist/runtime-UKBJQFXM.mjs +0 -57
  50. package/dist/scaffold-VV3KTYGO.mjs +0 -139
  51. package/dist/security-JR4P7L2C.mjs +0 -71
@@ -1,26 +1,26 @@
1
1
  import {
2
2
  loadProjectConfig,
3
3
  resolveGeneratedSchemaPath
4
- } from "./chunk-YEFJBN56.mjs";
4
+ } from "./chunk-4VYR2ARA.mjs";
5
5
  import {
6
6
  loadGeneratedProjectRegistry,
7
7
  relativeImportPath,
8
8
  renderAuthProviderRouteFiles,
9
9
  renderAuthRouteFiles,
10
+ renderBroadcastAuthSupportFrameworkFiles,
11
+ renderBroadcastInstallFrameworkFiles,
10
12
  renderFrameworkFiles,
11
13
  renderFrameworkRunner,
12
14
  renderGeneratedModelTypes,
13
- renderNextBroadcastAuthRoute,
14
- renderNextBroadcastConfigRoute,
15
- renderNextGeneratedBroadcastAuthRoute,
16
- renderNextGeneratedBroadcastConfigRoute,
15
+ renderManagedHostedAuthRouteFiles,
17
16
  renderNextGeneratedRealtimeDefinitions,
18
17
  renderNextHoloHelper,
19
- renderNextManagedHostedAuthRouteFiles,
20
18
  renderNextRuntimeBootstrap,
21
- renderSvelteHoloHelper,
22
19
  renderSvelteViteConfig
23
- } from "./chunk-J76GH2DR.mjs";
20
+ } from "./chunk-L3YATEQY.mjs";
21
+ import {
22
+ loadProjectPluginFrameworkDescriptors
23
+ } from "./chunk-3ICVWL2M.mjs";
24
24
  import {
25
25
  AUTH_CONFIG_FILE_NAMES,
26
26
  AUTH_SOCIAL_PROVIDER_PACKAGE_NAMES,
@@ -28,16 +28,31 @@ import {
28
28
  CACHE_CONFIG_FILE_NAMES,
29
29
  CORS_CONFIG_FILE_NAMES,
30
30
  DB_DRIVER_PACKAGE_NAMES,
31
+ ESBUILD_PACKAGE_VERSION,
31
32
  GENERATED_MODEL_TYPES_PATH,
33
+ HOLO_PACKAGE_VERSION,
34
+ IOREDIS_PACKAGE_VERSION,
32
35
  MAIL_CONFIG_FILE_NAMES,
33
36
  MEDIA_CONFIG_FILE_NAMES,
34
37
  NOTIFICATIONS_CONFIG_FILE_NAMES,
35
38
  QUEUE_CONFIG_FILE_NAMES,
36
39
  REDIS_CONFIG_FILE_NAMES,
40
+ SCAFFOLD_BASE_DEV_DEPENDENCY_VERSIONS,
41
+ SCAFFOLD_PACKAGE_MANAGER_VERSIONS,
37
42
  SECURITY_CONFIG_FILE_NAMES,
38
43
  SESSION_CONFIG_FILE_NAMES,
39
44
  SUPPORTED_AUTH_SOCIAL_PROVIDERS,
45
+ detectFrameworkDescriptorFromPackageJson,
46
+ frameworkDescriptorSupportsManagedBroadcastAuthRoute,
47
+ getFrameworkBroadcastPackages,
48
+ getFrameworkBroadcastPackagesFromDescriptor,
49
+ getFrameworkDescriptor,
50
+ getFrameworkDescriptorsWith,
51
+ getFrameworkRuntimeDependencyVersion,
52
+ getManagedFrameworkPackageNames,
53
+ importProjectModule,
40
54
  isSupportedCacheInstallerDriver,
55
+ isSupportedFrameworkId,
41
56
  isSupportedQueueInstallerDriver,
42
57
  normalizeScaffoldOptionalPackages,
43
58
  pathExists,
@@ -45,7 +60,7 @@ import {
45
60
  resolveFirstExistingPath,
46
61
  sanitizePackageName,
47
62
  writeTextFile
48
- } from "./chunk-ILU426CF.mjs";
63
+ } from "./chunk-PVVGQSUP.mjs";
49
64
 
50
65
  // src/project/scaffold.ts
51
66
  import { mkdir as mkdir3, readdir as readdir2 } from "fs/promises";
@@ -55,224 +70,16 @@ import { loadConfigDirectory as loadConfigDirectory2 } from "@holo-js/config";
55
70
  // src/project/scaffold/config-renderers.ts
56
71
  import { appendFile, mkdir } from "fs/promises";
57
72
  import { extname, resolve as resolve2 } from "path";
58
- import { holoStorageDefaults } from "@holo-js/config";
73
+ import { holoStorageDefaults } from "@holo-js/storage";
59
74
 
60
75
  // src/project/scaffold/dependencies.ts
61
76
  import { resolve } from "path";
62
- import { loadConfigDirectory } from "@holo-js/config";
63
-
64
- // package.json
65
- var package_default = {
66
- name: "@holo-js/cli",
67
- version: "0.2.6",
68
- description: "Holo-JS Framework - project creation, discovery, and operational CLI",
69
- type: "module",
70
- license: "MIT",
71
- bin: {
72
- holo: "./dist/bin/holo.mjs",
73
- "holo-js": "./dist/bin/holo.mjs"
74
- },
75
- exports: {
76
- ".": {
77
- types: "./dist/index.d.ts",
78
- import: "./dist/index.mjs"
79
- }
80
- },
81
- main: "./dist/index.mjs",
82
- types: "./dist/index.d.ts",
83
- files: [
84
- "dist"
85
- ],
86
- scripts: {
87
- build: "node ../../scripts/generate-cli-workspace-catalog.mjs && tsup",
88
- stub: "node ../../scripts/generate-cli-workspace-catalog.mjs && tsup --watch",
89
- typecheck: "tsc -p tsconfig.json --noEmit",
90
- test: "vitest --run && HOLO_CLI_INCLUDE_INTEGRATION=1 vitest --run tests/cli.test.ts",
91
- "test:integration": "HOLO_CLI_INCLUDE_INTEGRATION=1 vitest --run tests/cli.test.ts"
92
- },
93
- dependencies: {
94
- "@clack/prompts": "catalog:",
95
- "@holo-js/cache-db": "catalog:",
96
- "@holo-js/config": "catalog:",
97
- "@holo-js/core": "catalog:",
98
- "@holo-js/db": "catalog:",
99
- esbuild: "catalog:",
100
- inflection: "catalog:"
101
- },
102
- devDependencies: {
103
- "@holo-js/events": "catalog:",
104
- "@holo-js/queue": "catalog:",
105
- "@holo-js/queue-db": "catalog:",
106
- "@types/node": "catalog:",
107
- tsup: "catalog:",
108
- typescript: "catalog:",
109
- vitest: "catalog:"
110
- },
111
- engines: {
112
- node: ">=20.11.0"
113
- }
114
- };
115
-
116
- // src/generated/workspaceCatalog.ts
117
- var WORKSPACE_CATALOG = Object.freeze({
118
- "@clerk/backend": "^3.4.7",
119
- "@clack/prompts": "^1.5.1",
120
- "@eslint/js": "^9.17.0",
121
- "@holo-js/adapter-next": "^0.2.6",
122
- "@holo-js/adapter-nuxt": "^0.2.6",
123
- "@holo-js/adapter-sveltekit": "^0.2.6",
124
- "@holo-js/auth": "^0.2.6",
125
- "@holo-js/auth-clerk": "^0.2.6",
126
- "@holo-js/auth-social": "^0.2.6",
127
- "@holo-js/auth-social-apple": "^0.2.6",
128
- "@holo-js/auth-social-discord": "^0.2.6",
129
- "@holo-js/auth-social-facebook": "^0.2.6",
130
- "@holo-js/auth-social-github": "^0.2.6",
131
- "@holo-js/auth-social-google": "^0.2.6",
132
- "@holo-js/auth-social-linkedin": "^0.2.6",
133
- "@holo-js/auth-workos": "^0.2.6",
134
- "@holo-js/authorization": "^0.2.6",
135
- "@holo-js/broadcast": "^0.2.6",
136
- "@holo-js/cache": "^0.2.6",
137
- "@holo-js/cache-db": "^0.2.6",
138
- "@holo-js/cache-redis": "^0.2.6",
139
- "@holo-js/cli": "^0.2.6",
140
- "@holo-js/config": "^0.2.6",
141
- "@holo-js/core": "^0.2.6",
142
- "@holo-js/db": "^0.2.6",
143
- "@holo-js/db-mysql": "^0.2.6",
144
- "@holo-js/db-postgres": "^0.2.6",
145
- "@holo-js/db-sqlite": "^0.2.6",
146
- "@holo-js/events": "^0.2.6",
147
- "@holo-js/flux": "^0.2.6",
148
- "@holo-js/flux-react": "^0.2.6",
149
- "@holo-js/flux-svelte": "^0.2.6",
150
- "@holo-js/flux-vue": "^0.2.6",
151
- "@holo-js/forms": "^0.2.6",
152
- "@holo-js/mail": "^0.2.6",
153
- "@holo-js/media": "^0.2.6",
154
- "@holo-js/notifications": "^0.2.6",
155
- "@holo-js/queue": "^0.2.6",
156
- "@holo-js/queue-db": "^0.2.6",
157
- "@holo-js/queue-redis": "^0.2.6",
158
- "@holo-js/realtime": "^0.2.6",
159
- "@holo-js/security": "^0.2.6",
160
- "@holo-js/session": "^0.2.6",
161
- "@holo-js/storage": "^0.2.6",
162
- "@holo-js/storage-s3": "^0.2.6",
163
- "@holo-js/validation": "^0.2.6",
164
- "@nuxt/kit": "^4.4.4",
165
- "@nuxt/module-builder": "^1.0.2",
166
- "@sveltejs/adapter-node": "^5.5.4",
167
- "@sveltejs/kit": "^2.59.1",
168
- "@sveltejs/vite-plugin-svelte": "^7.1.0",
169
- "@types/better-sqlite3": "^7.6.12",
170
- "@types/node": "^22.10.2",
171
- "@types/pg": "^8.11.0",
172
- "@types/react": "^19.2.14",
173
- "@types/react-dom": "^19.2.3",
174
- "@types/react-test-renderer": "^19.1.0",
175
- "@types/ws": "^8.18.1",
176
- "@vitest/coverage-istanbul": "^4.1.5",
177
- "@vitest/coverage-v8": "^4.1.5",
178
- "better-sqlite3": "^11.7.0",
179
- "bullmq": "^5.71.0",
180
- "create-holo-js": "^0.2.6",
181
- "esbuild": "^0.27.4",
182
- "eslint": "^9.17.0",
183
- "fast-check": "^4.5.3",
184
- "globals": "^15.14.0",
185
- "h3": "^1.15.11",
186
- "inflection": "^3.0.2",
187
- "ioredis": "^5.4.2",
188
- "mysql2": "^3.17.1",
189
- "next": "^16.2.4",
190
- "nodemailer": "^6.10.1",
191
- "nuxt": "^4.4.4",
192
- "pg": "^8.13.0",
193
- "playwright": "^1.57.0",
194
- "react": "^19.2.6",
195
- "react-dom": "^19.2.6",
196
- "react-test-renderer": "^19.2.6",
197
- "sharp": "^0.34.4",
198
- "svelte": "^5.55.5",
199
- "svelte-check": "^4.4.6",
200
- "tslib": "^2.8.1",
201
- "tsup": "^8.3.5",
202
- "typescript": "^5.7.2",
203
- "typescript-eslint": "^8.30.1",
204
- "ulid": "^3.0.1",
205
- "uuid": "^12.0.0",
206
- "valibot": "^1.1.0",
207
- "vite": "^8.0.10",
208
- "vitepress": "^1.6.3",
209
- "vitest": "^4.1.5",
210
- "vue": "^3.5.13",
211
- "vue-router": "^5.0.4",
212
- "vue-tsc": "^2.2.0",
213
- "ws": "^8.18.3"
214
- });
215
-
216
- // src/metadata.ts
217
- var HOLO_PACKAGE_VERSION = package_default.version;
218
- var HOLO_PACKAGE_RANGE = `^${HOLO_PACKAGE_VERSION}`;
219
- function catalogVersion(packageName) {
220
- return WORKSPACE_CATALOG[packageName];
221
- }
222
- var ESBUILD_PACKAGE_VERSION = catalogVersion("esbuild");
223
- var SCAFFOLD_PACKAGE_MANAGER_VERSIONS = Object.freeze({
224
- npm: "npm@latest",
225
- pnpm: "pnpm@latest",
226
- yarn: "yarn@stable",
227
- bun: "bun@1.3.9"
228
- });
229
- var SCAFFOLD_FRAMEWORK_VERSIONS = Object.freeze({
230
- nuxt: catalogVersion("nuxt"),
231
- next: catalogVersion("next"),
232
- sveltekit: catalogVersion("@sveltejs/kit")
233
- });
234
- var SCAFFOLD_NEXT_REACT_VERSIONS = Object.freeze({
235
- react: catalogVersion("react"),
236
- "react-dom": catalogVersion("react-dom"),
237
- "@types/react": catalogVersion("@types/react"),
238
- "@types/react-dom": catalogVersion("@types/react-dom")
239
- });
240
- var SCAFFOLD_BASE_DEV_DEPENDENCY_VERSIONS = Object.freeze({
241
- typescript: catalogVersion("typescript"),
242
- "@types/node": catalogVersion("@types/node"),
243
- eslint: catalogVersion("eslint")
244
- });
245
- var SCAFFOLD_NUXT_DEPENDENCY_VERSIONS = Object.freeze({
246
- vue: catalogVersion("vue"),
247
- "vue-router": catalogVersion("vue-router"),
248
- "vue-tsc": catalogVersion("vue-tsc")
249
- });
250
- var SCAFFOLD_SVELTEKIT_DEPENDENCY_VERSIONS = Object.freeze({
251
- "@sveltejs/adapter-node": catalogVersion("@sveltejs/adapter-node"),
252
- "@sveltejs/vite-plugin-svelte": catalogVersion("@sveltejs/vite-plugin-svelte"),
253
- svelte: catalogVersion("svelte"),
254
- "svelte-check": catalogVersion("svelte-check"),
255
- vite: catalogVersion("vite")
256
- });
257
- var IOREDIS_PACKAGE_VERSION = catalogVersion("ioredis");
258
- var SCAFFOLD_FRAMEWORK_ADAPTER_VERSIONS = Object.freeze({
259
- nuxt: HOLO_PACKAGE_RANGE,
260
- next: HOLO_PACKAGE_RANGE,
261
- sveltekit: HOLO_PACKAGE_RANGE
262
- });
263
- var SCAFFOLD_FRAMEWORK_RUNTIME_VERSIONS = Object.freeze({
264
- nuxt: {
265
- "@holo-js/storage": HOLO_PACKAGE_RANGE
266
- },
267
- next: {
268
- "@holo-js/storage": HOLO_PACKAGE_RANGE
269
- },
270
- sveltekit: {
271
- "@holo-js/storage": HOLO_PACKAGE_RANGE
272
- }
273
- });
274
-
275
- // src/project/scaffold/dependencies.ts
77
+ import {
78
+ configureEnvRuntime,
79
+ loadConfigDirectory,
80
+ loadEnvironment,
81
+ resolveEnvPlaceholders
82
+ } from "@holo-js/config";
276
83
  function normalizeDependencyMap(value) {
277
84
  if (!value || typeof value !== "object" || Array.isArray(value)) {
278
85
  return {};
@@ -295,6 +102,48 @@ function resolveManagedHoloPackageVersion(packageName, dependencies, devDependen
295
102
  }).find(([dependencyName, dependencyVersion]) => dependencyName.startsWith("@holo-js/") && isWorkspaceDependencyVersion(dependencyVersion))?.[1];
296
103
  return workspaceVersion ?? `^${HOLO_PACKAGE_VERSION}`;
297
104
  }
105
+ function resolveQueueConfigModuleValue(moduleValue) {
106
+ if (!moduleValue || typeof moduleValue !== "object" || Array.isArray(moduleValue)) {
107
+ return void 0;
108
+ }
109
+ const exports = moduleValue;
110
+ const value = typeof exports.default !== "undefined" ? exports.default : exports.queue;
111
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
112
+ return void 0;
113
+ }
114
+ return value;
115
+ }
116
+ async function loadQueueConfigFile(projectRoot, queueConfigPath) {
117
+ const environment = await loadEnvironment({
118
+ cwd: projectRoot,
119
+ processEnv: process.env
120
+ });
121
+ const previousEnvEntries = /* @__PURE__ */ new Map();
122
+ try {
123
+ configureEnvRuntime(environment.values);
124
+ for (const [key, value] of Object.entries(environment.values)) {
125
+ previousEnvEntries.set(key, process.env[key]);
126
+ process.env[key] = value;
127
+ }
128
+ const rawQueueConfig = resolveQueueConfigModuleValue(await importProjectModule(projectRoot, queueConfigPath));
129
+ if (!rawQueueConfig) {
130
+ return void 0;
131
+ }
132
+ return resolveEnvPlaceholders(rawQueueConfig, environment.values);
133
+ } finally {
134
+ configureEnvRuntime(void 0);
135
+ for (const [key, value] of previousEnvEntries) {
136
+ if (typeof value === "string") {
137
+ process.env[key] = value;
138
+ continue;
139
+ }
140
+ Reflect.deleteProperty(process.env, key);
141
+ }
142
+ }
143
+ }
144
+ function queueConnectionUsesDriver(connection, driver) {
145
+ return typeof connection === "object" && connection !== null && !Array.isArray(connection) && connection.driver === driver;
146
+ }
298
147
  async function readPackageJsonDependencyState(projectRoot) {
299
148
  const packageJsonPath = resolve(projectRoot, "package.json");
300
149
  const existing = await readTextFile(packageJsonPath);
@@ -373,16 +222,16 @@ function registryHasAuthorizationDefinitions(registry) {
373
222
  return (registry?.authorizationPolicies.length ?? 0) > 0 || (registry?.authorizationAbilities.length ?? 0) > 0;
374
223
  }
375
224
  function authConfigUsesSocialProviders(loaded) {
376
- return Object.keys(loaded.auth.social).length > 0;
225
+ return Object.keys(loaded.auth?.social ?? {}).length > 0;
377
226
  }
378
227
  function authConfigUsesWorkosProviders(loaded) {
379
- return Object.entries(loaded.auth.workos).some(([name, provider]) => name !== "provider" && typeof provider === "object" && provider !== null);
228
+ return Object.entries(loaded.auth?.workos ?? {}).some(([name, provider]) => name !== "provider" && typeof provider === "object" && provider !== null);
380
229
  }
381
230
  function authConfigUsesClerkProviders(loaded) {
382
- return Object.keys(loaded.auth.clerk).length > 0;
231
+ return Object.keys(loaded.auth?.clerk ?? {}).length > 0;
383
232
  }
384
233
  function mailConfigUsesQueue(loaded) {
385
- return loaded.mail.queue.queued || Object.values(loaded.mail.mailers).some((mailer) => mailer.queue.queued);
234
+ return loaded.mail?.queue.queued === true || Object.values(loaded.mail?.mailers ?? {}).some((mailer) => mailer.queue.queued);
386
235
  }
387
236
  async function projectHasAuthorizationScaffold(projectRoot) {
388
237
  const project = await loadProjectConfig(projectRoot);
@@ -427,7 +276,12 @@ async function syncManagedDriverDependencies(projectRoot, registry) {
427
276
  } = await readPackageJsonDependencyState(projectRoot);
428
277
  const cachePackageInstalled = typeof dependencies["@holo-js/cache"] !== "undefined" || typeof devDependencies["@holo-js/cache"] !== "undefined";
429
278
  const cacheDesired = cacheConfigured || cachePackageInstalled;
279
+ const framework = await detectProjectFrameworkDescriptor(projectRoot, dependencies, devDependencies);
430
280
  requiredPackages.add("@holo-js/core");
281
+ requiredPackages.add("@holo-js/kernel");
282
+ if (framework) {
283
+ requiredPackages.add(framework.adapterPackage);
284
+ }
431
285
  for (const connection of Object.values(loaded.database.connections)) {
432
286
  const inferredDriver = inferConnectionDriver(connection);
433
287
  if (inferredDriver) {
@@ -444,7 +298,7 @@ async function syncManagedDriverDependencies(projectRoot, registry) {
444
298
  requiredPackages.add("@holo-js/auth");
445
299
  if (authConfigUsesSocialProviders(loaded)) {
446
300
  requiredPackages.add("@holo-js/auth-social");
447
- for (const [providerName, provider] of Object.entries(loaded.auth.social)) {
301
+ for (const [providerName, provider] of Object.entries(loaded.auth?.social ?? {})) {
448
302
  if (typeof provider.runtime === "string" && provider.runtime.trim()) {
449
303
  continue;
450
304
  }
@@ -471,7 +325,7 @@ async function syncManagedDriverDependencies(projectRoot, registry) {
471
325
  requiredPackages.add("@holo-js/cache");
472
326
  }
473
327
  if (cacheConfigured) {
474
- const cacheDrivers = Object.values(loaded.cache.drivers);
328
+ const cacheDrivers = Object.values(loaded.cache?.drivers ?? {});
475
329
  if (cacheDrivers.some((driver) => driver.driver === "redis")) {
476
330
  requiredPackages.add("@holo-js/cache-redis");
477
331
  }
@@ -485,16 +339,10 @@ async function syncManagedDriverDependencies(projectRoot, registry) {
485
339
  if (broadcastConfigured || registryHasBroadcastDefinitions(discoveredRegistry) || hasRealtimeScaffold) {
486
340
  requiredPackages.add("@holo-js/broadcast");
487
341
  requiredPackages.add("@holo-js/flux");
488
- const framework = detectProjectFrameworkFromPackageJson(dependencies, devDependencies);
489
- if (framework === "next") {
490
- requiredPackages.add("@holo-js/flux-react");
491
- requiredPackages.add("@holo-js/adapter-next");
492
- } else if (framework === "nuxt") {
493
- requiredPackages.add("@holo-js/flux-vue");
494
- requiredPackages.add("@holo-js/adapter-nuxt");
495
- } else if (framework === "sveltekit") {
496
- requiredPackages.add("@holo-js/flux-svelte");
497
- requiredPackages.add("@holo-js/adapter-sveltekit");
342
+ if (framework) {
343
+ for (const packageName of getFrameworkBroadcastPackagesFromDescriptor(framework)) {
344
+ requiredPackages.add(packageName);
345
+ }
498
346
  }
499
347
  }
500
348
  if (hasRealtimeScaffold) {
@@ -507,14 +355,14 @@ async function syncManagedDriverDependencies(projectRoot, registry) {
507
355
  requiredPackages.add("@holo-js/events");
508
356
  requiredPackages.add("@holo-js/queue");
509
357
  }
510
- if (queueConfigured || registryHasJobs(discoveredRegistry) || mailConfigUsesQueue(loaded)) {
358
+ if (queueConfigured || registryHasJobs(discoveredRegistry) || mailConfigured && mailConfigUsesQueue(loaded)) {
511
359
  requiredPackages.add("@holo-js/queue");
512
360
  if (queueConfigured) {
513
- const queueConnections = Object.values(loaded.queue.connections);
361
+ const queueConnections = Object.values(loaded.queue?.connections ?? {});
514
362
  if (queueConnections.some((connection) => connection.driver === "redis")) {
515
363
  requiredPackages.add("@holo-js/queue-redis");
516
364
  }
517
- if (queueConnections.some((connection) => connection.driver === "database") || loaded.queue.failed !== false) {
365
+ if (queueConnections.some((connection) => connection.driver === "database") || loaded.queue?.failed !== false) {
518
366
  requiredPackages.add("@holo-js/queue-db");
519
367
  }
520
368
  }
@@ -524,13 +372,14 @@ async function syncManagedDriverDependencies(projectRoot, registry) {
524
372
  }
525
373
  if (storageConfigured) {
526
374
  requiredPackages.add("@holo-js/storage");
527
- if (Object.values(loaded.storage.disks).some((disk) => disk.driver === "s3")) {
375
+ if (Object.values(loaded.storage?.disks ?? {}).some((disk) => disk.driver === "s3")) {
528
376
  requiredPackages.add("@holo-js/storage-s3");
529
377
  }
530
378
  }
531
379
  let changed = false;
532
380
  const removableManagedPackages = /* @__PURE__ */ new Set([
533
381
  "@holo-js/core",
382
+ "@holo-js/kernel",
534
383
  ...Object.values(DB_DRIVER_PACKAGE_NAMES),
535
384
  "@holo-js/auth",
536
385
  "@holo-js/auth-clerk",
@@ -542,9 +391,7 @@ async function syncManagedDriverDependencies(projectRoot, registry) {
542
391
  "@holo-js/cache-redis",
543
392
  "@holo-js/events",
544
393
  "@holo-js/flux",
545
- "@holo-js/flux-react",
546
- "@holo-js/flux-svelte",
547
- "@holo-js/flux-vue",
394
+ ...getManagedFrameworkPackageNames(),
548
395
  "@holo-js/mail",
549
396
  "@holo-js/media",
550
397
  "@holo-js/notifications",
@@ -589,18 +436,17 @@ async function syncManagedDriverDependencies(projectRoot, registry) {
589
436
  async function upsertQueuePackageDependency(projectRoot, driver) {
590
437
  const { packageJsonPath, parsed, dependencies, devDependencies } = await readPackageJsonDependencyState(projectRoot);
591
438
  const queueConfigPath = await resolveFirstExistingPath(projectRoot, ["config/queue.ts", "config/queue.mts", "config/queue.js", "config/queue.mjs", "config/queue.cts", "config/queue.cjs"]);
592
- const loadedQueueConfig = queueConfigPath ? loadConfigDirectory(projectRoot, {
593
- preferCache: false,
594
- processEnv: process.env
595
- }).then((config) => config.queue).catch(() => void 0) : Promise.resolve(void 0);
439
+ const loadedQueueConfig = queueConfigPath ? loadQueueConfigFile(projectRoot, queueConfigPath).catch(() => void 0) : Promise.resolve(void 0);
596
440
  const nextVersion = resolveManagedHoloPackageVersion("@holo-js/queue", dependencies, devDependencies);
597
441
  const nextQueueDbVersion = resolveManagedHoloPackageVersion("@holo-js/queue-db", dependencies, devDependencies);
598
442
  const nextQueueRedisVersion = resolveManagedHoloPackageVersion("@holo-js/queue-redis", dependencies, devDependencies);
599
443
  const nextEsbuildVersion = ESBUILD_PACKAGE_VERSION;
600
444
  const queueConfig = typeof driver === "undefined" ? await loadedQueueConfig : void 0;
601
- const resolvedQueueDriver = driver && driver !== "sync" ? driver : queueConfig?.connections[queueConfig.default]?.driver ?? driver;
602
- const requiresQueueDb = resolvedQueueDriver === "database" || (queueConfig?.failed ?? false) !== false || Object.values(queueConfig?.connections ?? {}).some((connection) => connection.driver === "database");
603
- const requiresQueueRedis = resolvedQueueDriver === "redis" || Object.values(queueConfig?.connections ?? {}).some((connection) => connection.driver === "redis");
445
+ const queueConnections = Object.values(queueConfig?.connections ?? {});
446
+ const configuredDefaultQueueDriver = queueConfig?.default ? queueConfig.connections?.[queueConfig.default]?.driver : void 0;
447
+ const resolvedQueueDriver = driver && driver !== "sync" ? driver : configuredDefaultQueueDriver ?? driver;
448
+ const requiresQueueDb = resolvedQueueDriver === "database" || (queueConfig ? (queueConfig.failed ?? true) !== false : false) || queueConnections.some((connection) => queueConnectionUsesDriver(connection, "database"));
449
+ const requiresQueueRedis = resolvedQueueDriver === "redis" || queueConnections.some((connection) => queueConnectionUsesDriver(connection, "redis"));
604
450
  const currentVersion = dependencies["@holo-js/queue"];
605
451
  const currentQueueDbVersion = dependencies["@holo-js/queue-db"];
606
452
  const currentQueueRedisVersion = dependencies["@holo-js/queue-redis"];
@@ -713,44 +559,31 @@ async function upsertCachePackageDependencies(projectRoot, driver = "file") {
713
559
  return true;
714
560
  }
715
561
  function detectProjectFrameworkFromPackageJson(dependencies, devDependencies) {
716
- if (dependencies.next || devDependencies.next) {
717
- return "next";
718
- }
719
- if (dependencies.nuxt || devDependencies.nuxt) {
720
- return "nuxt";
721
- }
722
- if (dependencies["@sveltejs/kit"] || devDependencies["@sveltejs/kit"]) {
723
- return "sveltekit";
724
- }
725
- return void 0;
562
+ return detectFrameworkDescriptorFromPackageJson(dependencies, devDependencies)?.id;
563
+ }
564
+ async function detectProjectFrameworkDescriptor(projectRoot, dependencies, devDependencies) {
565
+ const pluginDescriptors = await loadProjectPluginFrameworkDescriptors(projectRoot);
566
+ return detectFrameworkDescriptorFromPackageJson(
567
+ dependencies,
568
+ devDependencies,
569
+ getFrameworkDescriptorsWith(pluginDescriptors)
570
+ );
726
571
  }
727
572
  async function upsertBroadcastPackageDependencies(projectRoot) {
728
573
  const { packageJsonPath, parsed, dependencies, devDependencies } = await readPackageJsonDependencyState(projectRoot);
729
574
  const nextVersion = resolveManagedHoloPackageVersion("@holo-js/broadcast", dependencies, devDependencies);
730
- const framework = detectProjectFrameworkFromPackageJson(dependencies, devDependencies);
575
+ const framework = await detectProjectFrameworkDescriptor(projectRoot, dependencies, devDependencies);
731
576
  let changed = false;
732
577
  const requestedPackages = /* @__PURE__ */ new Set([
733
578
  "@holo-js/broadcast",
734
579
  "@holo-js/flux"
735
580
  ]);
736
- if (framework === "next") {
737
- requestedPackages.add("@holo-js/flux-react");
738
- requestedPackages.add("@holo-js/adapter-next");
739
- } else if (framework === "nuxt") {
740
- requestedPackages.add("@holo-js/flux-vue");
741
- requestedPackages.add("@holo-js/adapter-nuxt");
742
- } else if (framework === "sveltekit") {
743
- requestedPackages.add("@holo-js/flux-svelte");
744
- requestedPackages.add("@holo-js/adapter-sveltekit");
745
- }
746
- const frameworkPackages = /* @__PURE__ */ new Set([
747
- "@holo-js/flux-react",
748
- "@holo-js/adapter-next",
749
- "@holo-js/flux-vue",
750
- "@holo-js/adapter-nuxt",
751
- "@holo-js/flux-svelte",
752
- "@holo-js/adapter-sveltekit"
753
- ]);
581
+ if (framework) {
582
+ for (const packageName of getFrameworkBroadcastPackagesFromDescriptor(framework)) {
583
+ requestedPackages.add(packageName);
584
+ }
585
+ }
586
+ const frameworkPackages = new Set(getManagedFrameworkPackageNames());
754
587
  const managedPackages = /* @__PURE__ */ new Set([
755
588
  ...requestedPackages,
756
589
  ...frameworkPackages
@@ -773,13 +606,13 @@ async function upsertBroadcastPackageDependencies(projectRoot) {
773
606
  if (!changed) {
774
607
  return {
775
608
  updated: false,
776
- framework
609
+ framework: framework?.id
777
610
  };
778
611
  }
779
612
  await writePackageJsonDependencyState(packageJsonPath, parsed, dependencies, devDependencies);
780
613
  return {
781
614
  updated: true,
782
- framework
615
+ framework: framework?.id
783
616
  };
784
617
  }
785
618
  async function upsertAuthPackageDependencies(projectRoot, features = {}) {
@@ -865,7 +698,8 @@ async function upsertRealtimePackageDependency(projectRoot) {
865
698
  // src/project/scaffold/config-renderers.ts
866
699
  function renderStorageConfig() {
867
700
  return [
868
- "import { defineStorageConfig, env } from '@holo-js/config'",
701
+ "import { defineStorageConfig } from '@holo-js/storage'",
702
+ "import { env } from '@holo-js/config'",
869
703
  "",
870
704
  "export default defineStorageConfig({",
871
705
  ` defaultDisk: env('STORAGE_DEFAULT_DISK', '${holoStorageDefaults.defaultDisk}'),`,
@@ -887,7 +721,7 @@ function renderStorageConfig() {
887
721
  }
888
722
  function renderMediaConfig() {
889
723
  return [
890
- "import { defineMediaConfig } from '@holo-js/config'",
724
+ "import { defineMediaConfig } from '@holo-js/media/config'",
891
725
  "",
892
726
  "export default defineMediaConfig({})",
893
727
  ""
@@ -898,7 +732,8 @@ function renderQueueConfig(options = {}) {
898
732
  const defaultDatabaseConnection = options.defaultDatabaseConnection?.trim() || "default";
899
733
  if (driver === "redis") {
900
734
  return [
901
- "import { defineQueueConfig, env } from '@holo-js/config'",
735
+ "import { defineQueueConfig } from '@holo-js/queue'",
736
+ "import { env } from '@holo-js/config'",
902
737
  "",
903
738
  "export default defineQueueConfig({",
904
739
  " default: 'redis',",
@@ -918,7 +753,7 @@ function renderQueueConfig(options = {}) {
918
753
  }
919
754
  if (driver === "database") {
920
755
  return [
921
- "import { defineQueueConfig } from '@holo-js/config'",
756
+ "import { defineQueueConfig } from '@holo-js/queue'",
922
757
  "",
923
758
  "export default defineQueueConfig({",
924
759
  " default: 'database',",
@@ -942,7 +777,7 @@ function renderQueueConfig(options = {}) {
942
777
  ].join("\n");
943
778
  }
944
779
  return [
945
- "import { defineQueueConfig } from '@holo-js/config'",
780
+ "import { defineQueueConfig } from '@holo-js/queue'",
946
781
  "",
947
782
  "export default defineQueueConfig({",
948
783
  " default: 'sync',",
@@ -959,7 +794,8 @@ function renderQueueConfig(options = {}) {
959
794
  }
960
795
  function renderCacheConfig(driver = "file", defaultDatabaseConnection = "default", defaultRedisConnection = "default") {
961
796
  const lines = [
962
- "import { defineCacheConfig, env } from '@holo-js/config'",
797
+ "import { defineCacheConfig } from '@holo-js/cache'",
798
+ "import { env } from '@holo-js/config'",
963
799
  "",
964
800
  "export default defineCacheConfig({",
965
801
  ` default: '${driver}',`,
@@ -1002,7 +838,8 @@ function renderCacheConfig(driver = "file", defaultDatabaseConnection = "default
1002
838
  }
1003
839
  function renderRedisConfig() {
1004
840
  return [
1005
- "import { defineRedisConfig, env } from '@holo-js/config'",
841
+ "import { env } from '@holo-js/config'",
842
+ "import { defineRedisConfig } from '@holo-js/kernel'",
1006
843
  "",
1007
844
  "export default defineRedisConfig({",
1008
845
  " default: 'default',",
@@ -1030,7 +867,7 @@ async function ensureRedisConfigFile(projectRoot) {
1030
867
  }
1031
868
  function renderNotificationsConfig() {
1032
869
  return [
1033
- "import { defineNotificationsConfig } from '@holo-js/config'",
870
+ "import { defineNotificationsConfig } from '@holo-js/notifications'",
1034
871
  "",
1035
872
  "export default defineNotificationsConfig({",
1036
873
  " table: 'notifications',",
@@ -1043,7 +880,8 @@ function renderNotificationsConfig() {
1043
880
  }
1044
881
  function renderMailConfig() {
1045
882
  return [
1046
- "import { defineMailConfig, env } from '@holo-js/config'",
883
+ "import { defineMailConfig } from '@holo-js/mail'",
884
+ "import { env } from '@holo-js/config'",
1047
885
  "",
1048
886
  "export default defineMailConfig({",
1049
887
  " default: env('MAIL_MAILER', 'preview'),",
@@ -1110,7 +948,8 @@ function renderSecurityConfig() {
1110
948
  }
1111
949
  function renderCorsConfig() {
1112
950
  return [
1113
- "import { defineCorsConfig, env } from '@holo-js/config'",
951
+ "import { env } from '@holo-js/config'",
952
+ "import { defineCorsConfig } from '@holo-js/security'",
1114
953
  "",
1115
954
  "export default defineCorsConfig({",
1116
955
  " paths: ['/api/*', '/broadcasting/auth'],",
@@ -1166,12 +1005,16 @@ function renderBroadcastConfig(moduleFormat, includeAuthEndpoint, useTypeScriptS
1166
1005
  };
1167
1006
  if (moduleFormat === "cjs") {
1168
1007
  return [
1169
- "const { defineBroadcastConfig, env } = require('@holo-js/config')",
1008
+ "const { defineBroadcastConfig } = require('@holo-js/broadcast')",
1009
+ "const { env } = require('@holo-js/config')",
1170
1010
  "",
1171
1011
  `const broadcastScheme = ${renderBroadcastScheme()}`,
1172
1012
  "",
1173
1013
  "module.exports = defineBroadcastConfig({",
1174
1014
  " default: env('BROADCAST_CONNECTION', 'holo'),",
1015
+ " worker: {",
1016
+ " allowedOrigins: [env('APP_URL', 'http://localhost:3000')],",
1017
+ " },",
1175
1018
  " connections: {",
1176
1019
  " holo: {",
1177
1020
  " driver: 'holo',",
@@ -1202,12 +1045,16 @@ function renderBroadcastConfig(moduleFormat, includeAuthEndpoint, useTypeScriptS
1202
1045
  ].join("\n");
1203
1046
  }
1204
1047
  return [
1205
- "import { defineBroadcastConfig, env } from '@holo-js/config'",
1048
+ "import { defineBroadcastConfig } from '@holo-js/broadcast'",
1049
+ "import { env } from '@holo-js/config'",
1206
1050
  "",
1207
1051
  `const broadcastScheme = ${renderBroadcastScheme()}`,
1208
1052
  "",
1209
1053
  "export default defineBroadcastConfig({",
1210
1054
  " default: env('BROADCAST_CONNECTION', 'holo'),",
1055
+ " worker: {",
1056
+ " allowedOrigins: [env('APP_URL', 'http://localhost:3000')],",
1057
+ " },",
1211
1058
  " connections: {",
1212
1059
  " holo: {",
1213
1060
  " driver: 'holo',",
@@ -1309,11 +1156,12 @@ function renderBroadcastEnvFiles() {
1309
1156
  }
1310
1157
  async function syncBroadcastAuthSupportAfterAuthInstall(projectRoot) {
1311
1158
  const { dependencies, devDependencies } = await readPackageJsonDependencyState(projectRoot);
1312
- const framework = detectProjectFrameworkFromPackageJson(dependencies, devDependencies);
1313
- const canCreateBroadcastAuthRoute = framework === "next" || framework === "nuxt" || framework === "sveltekit";
1159
+ const framework = await detectProjectFrameworkDescriptor(projectRoot, dependencies, devDependencies);
1160
+ const canCreateBroadcastAuthRoute = frameworkDescriptorSupportsManagedBroadcastAuthRoute(framework);
1161
+ const frameworkId = framework && isSupportedFrameworkId(framework.id) ? framework.id : void 0;
1314
1162
  const authConfigPath = await resolveFirstExistingPath(projectRoot, AUTH_CONFIG_FILE_NAMES);
1315
1163
  const broadcastConfigPath = await resolveFirstExistingPath(projectRoot, BROADCAST_CONFIG_FILE_NAMES);
1316
- if (!authConfigPath || !broadcastConfigPath || !canCreateBroadcastAuthRoute) {
1164
+ if (!authConfigPath || !broadcastConfigPath || !frameworkId || !canCreateBroadcastAuthRoute) {
1317
1165
  return {
1318
1166
  updatedBroadcastConfig: false,
1319
1167
  createdBroadcastAuthRoute: false
@@ -1338,24 +1186,16 @@ async function syncBroadcastAuthSupportAfterAuthInstall(projectRoot) {
1338
1186
  updatedBroadcastConfig = true;
1339
1187
  }
1340
1188
  }
1341
- if (framework === "next") {
1342
- const authRoutePath = resolve2(projectRoot, "app/broadcasting/auth/route.ts");
1343
- const holoHelperPath = resolve2(projectRoot, ".holo-js/generated/next/holo.ts");
1344
- const generatedRoutePath = resolve2(projectRoot, ".holo-js/generated/next/broadcast-auth-route.ts");
1345
- if (!await pathExists(authRoutePath)) {
1346
- await writeTextFile(authRoutePath, renderNextBroadcastAuthRoute());
1189
+ for (const file of renderBroadcastAuthSupportFrameworkFiles(frameworkId)) {
1190
+ const filePath = resolve2(projectRoot, file.path);
1191
+ const generated = file.path.startsWith(".holo-js/generated/");
1192
+ if (!generated && await pathExists(filePath)) {
1193
+ continue;
1194
+ }
1195
+ if (!generated) {
1347
1196
  createdBroadcastAuthRoute = true;
1348
1197
  }
1349
- await writeTextFile(holoHelperPath, renderNextHoloHelper());
1350
- await writeTextFile(generatedRoutePath, renderNextGeneratedBroadcastAuthRoute());
1351
- return {
1352
- updatedBroadcastConfig,
1353
- createdBroadcastAuthRoute
1354
- };
1355
- }
1356
- if (framework === "sveltekit") {
1357
- const holoHelperPath = resolve2(projectRoot, ".holo-js/generated/sveltekit/holo.ts");
1358
- await writeTextFile(holoHelperPath, renderSvelteHoloHelper());
1198
+ await writeTextFile(filePath, file.contents);
1359
1199
  }
1360
1200
  return {
1361
1201
  updatedBroadcastConfig,
@@ -1364,7 +1204,8 @@ async function syncBroadcastAuthSupportAfterAuthInstall(projectRoot) {
1364
1204
  }
1365
1205
  function renderSessionConfig(defaultDatabaseConnection = "default") {
1366
1206
  return [
1367
- "import { defineSessionConfig, env } from '@holo-js/config'",
1207
+ "import { defineSessionConfig } from '@holo-js/session'",
1208
+ "import { env } from '@holo-js/config'",
1368
1209
  "",
1369
1210
  "const sessionSameSite = env('SESSION_SAME_SITE') === 'strict'",
1370
1211
  " ? 'strict'",
@@ -1409,10 +1250,17 @@ function renderAuthConfig(features = {}, moduleFormat = "esm") {
1409
1250
  };
1410
1251
  const socialEnabled = features.social === true || (features.socialProviders?.length ?? 0) > 0;
1411
1252
  const socialProviders = features.socialProviders && features.socialProviders.length > 0 ? features.socialProviders : socialEnabled ? ["google"] : [];
1412
- const lines = [
1413
- moduleFormat === "cjs" ? "module.exports = {" : "import { defineAuthConfig, env } from '@holo-js/config'",
1253
+ const envRequired = socialProviders.length > 0 || features.workos === true || features.clerk === true;
1254
+ const lines = moduleFormat === "cjs" ? [
1255
+ "module.exports = {",
1256
+ ""
1257
+ ] : [
1258
+ "import { defineAuthConfig } from '@holo-js/auth'",
1259
+ ...envRequired ? ["import { env } from '@holo-js/config'"] : [],
1414
1260
  "",
1415
- ...moduleFormat === "cjs" ? [] : ["export default defineAuthConfig({"],
1261
+ "export default defineAuthConfig({"
1262
+ ];
1263
+ lines.push(
1416
1264
  " defaults: {",
1417
1265
  " guard: 'web',",
1418
1266
  " passwords: 'users',",
@@ -1450,7 +1298,7 @@ function renderAuthConfig(features = {}, moduleFormat = "esm") {
1450
1298
  " required: false,",
1451
1299
  " route: '/verify-email',",
1452
1300
  " },"
1453
- ];
1301
+ );
1454
1302
  if (socialProviders.length > 0) {
1455
1303
  lines.push(" social: {");
1456
1304
  for (const provider of socialProviders) {
@@ -1545,12 +1393,42 @@ function mergeInstalledAuthFeatures(current, requested) {
1545
1393
  }
1546
1394
 
1547
1395
  // src/project/scaffold/framework.ts
1548
- import { mkdir as mkdir2, readdir, writeFile } from "fs/promises";
1396
+ import { mkdir as mkdir2, writeFile } from "fs/promises";
1549
1397
  import { dirname, resolve as resolve3 } from "path";
1550
1398
  import {
1551
- normalizeHoloProjectConfig,
1552
- renderGeneratedSchemaPlaceholder
1553
- } from "@holo-js/db";
1399
+ normalizeHoloProjectConfig as normalizeHoloProjectConfig2
1400
+ } from "@holo-js/kernel";
1401
+ import { renderGeneratedSchemaPlaceholder } from "@holo-js/db";
1402
+
1403
+ // src/fs-utils.ts
1404
+ import { readdir, stat } from "fs/promises";
1405
+
1406
+ // src/project.ts
1407
+ import { normalizeHoloProjectConfig } from "@holo-js/kernel";
1408
+
1409
+ // src/fs-utils.ts
1410
+ async function fileExists(path) {
1411
+ try {
1412
+ await stat(path);
1413
+ return true;
1414
+ } catch {
1415
+ return false;
1416
+ }
1417
+ }
1418
+ async function ensureAbsent(path) {
1419
+ const existing = await readTextFile(path);
1420
+ if (typeof existing !== "undefined") {
1421
+ throw new TypeError(`Refusing to overwrite existing file: ${path}`);
1422
+ }
1423
+ }
1424
+ async function ensureEmptyDirectory(path, displayPath = path) {
1425
+ const entries = await readdir(path).catch(() => []);
1426
+ if (entries.length > 0) {
1427
+ throw new Error(
1428
+ `The destination "${displayPath}" already exists and is not empty. Choose a different project name or empty that folder first.`
1429
+ );
1430
+ }
1431
+ }
1554
1432
 
1555
1433
  // src/project/scaffold/project-renderers.ts
1556
1434
  import {
@@ -1913,7 +1791,7 @@ function createNotificationsMigrationFiles(date = /* @__PURE__ */ new Date()) {
1913
1791
  }];
1914
1792
  }
1915
1793
  function resolveFrameworkDefaultUrl(framework) {
1916
- return framework === "sveltekit" ? "http://localhost:5173" : "http://localhost:3000";
1794
+ return framework ? getFrameworkDescriptor(framework).scaffold.defaultUrl : "http://localhost:3000";
1917
1795
  }
1918
1796
  function renderScaffoldAppConfig(projectName, framework) {
1919
1797
  const defaultUrl = resolveFrameworkDefaultUrl(framework);
@@ -1950,7 +1828,8 @@ function renderScaffoldDatabaseConfig(options) {
1950
1828
  const packageName = sanitizePackageName(options.projectName) || "holo-app";
1951
1829
  if (options.databaseDriver === "sqlite") {
1952
1830
  return [
1953
- "import { defineDatabaseConfig, env } from '@holo-js/config'",
1831
+ "import { defineDatabaseConfig } from '@holo-js/db'",
1832
+ "import { env } from '@holo-js/config'",
1954
1833
  "",
1955
1834
  "export default defineDatabaseConfig({",
1956
1835
  " defaultConnection: 'main',",
@@ -1968,7 +1847,8 @@ function renderScaffoldDatabaseConfig(options) {
1968
1847
  const username = options.databaseDriver === "mysql" ? "root" : "postgres";
1969
1848
  const schemaLine = options.databaseDriver === "postgres" ? " schema: env('DB_SCHEMA', 'public')," : void 0;
1970
1849
  return [
1971
- "import { defineDatabaseConfig, env } from '@holo-js/config'",
1850
+ "import { defineDatabaseConfig } from '@holo-js/db'",
1851
+ "import { env } from '@holo-js/config'",
1972
1852
  "",
1973
1853
  "export default defineDatabaseConfig({",
1974
1854
  " defaultConnection: 'main',",
@@ -2194,6 +2074,46 @@ function upsertEnvContents(existingContents, additions) {
2194
2074
  }
2195
2075
 
2196
2076
  // src/project/scaffold/workspace-renderers.ts
2077
+ function renderScaffoldEslintConfig() {
2078
+ return [
2079
+ "import js from '@eslint/js'",
2080
+ "import globals from 'globals'",
2081
+ "import tseslint from 'typescript-eslint'",
2082
+ "",
2083
+ "export default tseslint.config(",
2084
+ " {",
2085
+ " ignores: [",
2086
+ " '.holo-js/**',",
2087
+ " '.next/**',",
2088
+ " '.nuxt/**',",
2089
+ " '.output/**',",
2090
+ " '.svelte-kit/**',",
2091
+ " 'build/**',",
2092
+ " 'coverage/**',",
2093
+ " 'node_modules/**',",
2094
+ " ],",
2095
+ " },",
2096
+ " js.configs.recommended,",
2097
+ " ...tseslint.configs.recommended,",
2098
+ " {",
2099
+ " files: ['**/*.{js,mjs,cjs,ts,tsx,mts,cts}'],",
2100
+ " languageOptions: {",
2101
+ " globals: globals.node,",
2102
+ " },",
2103
+ " rules: {",
2104
+ " 'no-var': 'error',",
2105
+ " 'prefer-const': 'error',",
2106
+ " eqeqeq: ['error', 'always', { null: 'ignore' }],",
2107
+ " '@typescript-eslint/consistent-type-imports': ['error', {",
2108
+ " prefer: 'type-imports',",
2109
+ " fixStyle: 'inline-type-imports',",
2110
+ " }],",
2111
+ " },",
2112
+ " },",
2113
+ ")",
2114
+ ""
2115
+ ].join("\n");
2116
+ }
2197
2117
  function renderScaffoldGitignore() {
2198
2118
  return [
2199
2119
  "# Dependencies",
@@ -2254,13 +2174,14 @@ function renderScaffoldGitignore() {
2254
2174
  ].join("\n");
2255
2175
  }
2256
2176
  function renderScaffoldTsconfig(options) {
2257
- if (options.framework === "nuxt") {
2177
+ const framework = getFrameworkDescriptor(options.framework);
2178
+ if (framework.scaffold.tsconfig === "nuxt") {
2258
2179
  return `${JSON.stringify({
2259
2180
  extends: "./.nuxt/tsconfig.json"
2260
2181
  }, null, 2)}
2261
2182
  `;
2262
2183
  }
2263
- if (options.framework === "sveltekit") {
2184
+ if (framework.scaffold.tsconfig === "sveltekit") {
2264
2185
  return `${JSON.stringify({
2265
2186
  extends: "./.svelte-kit/tsconfig.json",
2266
2187
  compilerOptions: {
@@ -2301,14 +2222,15 @@ function renderScaffoldTsconfig(options) {
2301
2222
  `;
2302
2223
  }
2303
2224
  function renderVSCodeSettings(options) {
2304
- if (options.framework !== "nuxt" && options.framework !== "sveltekit") {
2225
+ const framework = getFrameworkDescriptor(options.framework);
2226
+ if (!framework.scaffold.vscodeVueHybridMode && framework.scaffold.tsconfig !== "sveltekit") {
2305
2227
  return void 0;
2306
2228
  }
2307
2229
  const settings = {
2308
2230
  "typescript.tsdk": "node_modules/typescript/lib",
2309
2231
  "typescript.enablePromptUseWorkspaceTsdk": true
2310
2232
  };
2311
- if (options.framework === "nuxt") {
2233
+ if (framework.scaffold.vscodeVueHybridMode) {
2312
2234
  settings["vue.server.hybridMode"] = true;
2313
2235
  }
2314
2236
  return `${JSON.stringify(settings, null, 2)}
@@ -2322,45 +2244,28 @@ function resolvePackageManagerVersion(value) {
2322
2244
  function renderScaffoldPackageJson(options) {
2323
2245
  const packageName = sanitizePackageName(options.projectName) || "holo-app";
2324
2246
  const optionalPackages = normalizeScaffoldOptionalPackages(options.optionalPackages);
2247
+ const framework = getFrameworkDescriptor(options.framework);
2325
2248
  const dependencies = {
2326
2249
  "@holo-js/cli": `^${HOLO_PACKAGE_VERSION}`,
2327
2250
  "@holo-js/config": `^${HOLO_PACKAGE_VERSION}`,
2328
2251
  "@holo-js/core": `^${HOLO_PACKAGE_VERSION}`,
2329
2252
  "@holo-js/db": `^${HOLO_PACKAGE_VERSION}`,
2253
+ "@holo-js/kernel": `^${HOLO_PACKAGE_VERSION}`,
2330
2254
  [DB_DRIVER_PACKAGE_NAMES[options.databaseDriver]]: `^${HOLO_PACKAGE_VERSION}`,
2331
- esbuild: ESBUILD_PACKAGE_VERSION
2255
+ esbuild: ESBUILD_PACKAGE_VERSION,
2256
+ ...framework.scaffold.dependencies
2332
2257
  };
2333
2258
  const devDependencies = {
2334
2259
  typescript: SCAFFOLD_BASE_DEV_DEPENDENCY_VERSIONS.typescript,
2335
2260
  "@types/node": SCAFFOLD_BASE_DEV_DEPENDENCY_VERSIONS["@types/node"],
2336
- eslint: SCAFFOLD_BASE_DEV_DEPENDENCY_VERSIONS.eslint
2261
+ "@eslint/js": SCAFFOLD_BASE_DEV_DEPENDENCY_VERSIONS["@eslint/js"],
2262
+ eslint: SCAFFOLD_BASE_DEV_DEPENDENCY_VERSIONS.eslint,
2263
+ globals: SCAFFOLD_BASE_DEV_DEPENDENCY_VERSIONS.globals,
2264
+ "typescript-eslint": SCAFFOLD_BASE_DEV_DEPENDENCY_VERSIONS["typescript-eslint"],
2265
+ ...framework.scaffold.devDependencies
2337
2266
  };
2338
- if (options.framework === "nuxt") {
2339
- dependencies.nuxt = SCAFFOLD_FRAMEWORK_VERSIONS.nuxt;
2340
- dependencies.vue = SCAFFOLD_NUXT_DEPENDENCY_VERSIONS.vue;
2341
- dependencies["vue-router"] = SCAFFOLD_NUXT_DEPENDENCY_VERSIONS["vue-router"];
2342
- dependencies["@holo-js/adapter-nuxt"] = SCAFFOLD_FRAMEWORK_ADAPTER_VERSIONS.nuxt;
2343
- devDependencies["vue-tsc"] = SCAFFOLD_NUXT_DEPENDENCY_VERSIONS["vue-tsc"];
2344
- }
2345
- if (options.framework === "next") {
2346
- dependencies.next = SCAFFOLD_FRAMEWORK_VERSIONS.next;
2347
- dependencies.react = SCAFFOLD_NEXT_REACT_VERSIONS.react;
2348
- dependencies["react-dom"] = SCAFFOLD_NEXT_REACT_VERSIONS["react-dom"];
2349
- dependencies["@holo-js/adapter-next"] = SCAFFOLD_FRAMEWORK_ADAPTER_VERSIONS.next;
2350
- devDependencies["@types/react"] = SCAFFOLD_NEXT_REACT_VERSIONS["@types/react"];
2351
- devDependencies["@types/react-dom"] = SCAFFOLD_NEXT_REACT_VERSIONS["@types/react-dom"];
2352
- }
2353
- if (options.framework === "sveltekit") {
2354
- dependencies["@holo-js/adapter-sveltekit"] = SCAFFOLD_FRAMEWORK_ADAPTER_VERSIONS.sveltekit;
2355
- dependencies["@sveltejs/adapter-node"] = SCAFFOLD_SVELTEKIT_DEPENDENCY_VERSIONS["@sveltejs/adapter-node"];
2356
- dependencies["@sveltejs/kit"] = SCAFFOLD_FRAMEWORK_VERSIONS.sveltekit;
2357
- dependencies["@sveltejs/vite-plugin-svelte"] = SCAFFOLD_SVELTEKIT_DEPENDENCY_VERSIONS["@sveltejs/vite-plugin-svelte"];
2358
- dependencies.svelte = SCAFFOLD_SVELTEKIT_DEPENDENCY_VERSIONS.svelte;
2359
- dependencies.vite = SCAFFOLD_SVELTEKIT_DEPENDENCY_VERSIONS.vite;
2360
- devDependencies["svelte-check"] = SCAFFOLD_SVELTEKIT_DEPENDENCY_VERSIONS["svelte-check"];
2361
- }
2362
2267
  if (optionalPackages.includes("storage")) {
2363
- dependencies["@holo-js/storage"] = SCAFFOLD_FRAMEWORK_RUNTIME_VERSIONS[options.framework]["@holo-js/storage"];
2268
+ dependencies["@holo-js/storage"] = getFrameworkRuntimeDependencyVersion(options.framework, "@holo-js/storage");
2364
2269
  }
2365
2270
  if (optionalPackages.includes("events")) {
2366
2271
  dependencies["@holo-js/events"] = `^${HOLO_PACKAGE_VERSION}`;
@@ -2392,12 +2297,8 @@ function renderScaffoldPackageJson(options) {
2392
2297
  if (optionalPackages.includes("broadcast") || optionalPackages.includes("realtime")) {
2393
2298
  dependencies["@holo-js/broadcast"] = `^${HOLO_PACKAGE_VERSION}`;
2394
2299
  dependencies["@holo-js/flux"] = `^${HOLO_PACKAGE_VERSION}`;
2395
- if (options.framework === "next") {
2396
- dependencies["@holo-js/flux-react"] = `^${HOLO_PACKAGE_VERSION}`;
2397
- } else if (options.framework === "nuxt") {
2398
- dependencies["@holo-js/flux-vue"] = `^${HOLO_PACKAGE_VERSION}`;
2399
- } else if (options.framework === "sveltekit") {
2400
- dependencies["@holo-js/flux-svelte"] = `^${HOLO_PACKAGE_VERSION}`;
2300
+ for (const packageName2 of getFrameworkBroadcastPackages(options.framework)) {
2301
+ dependencies[packageName2] = `^${HOLO_PACKAGE_VERSION}`;
2401
2302
  }
2402
2303
  }
2403
2304
  if (optionalPackages.includes("realtime")) {
@@ -2415,13 +2316,13 @@ function renderScaffoldPackageJson(options) {
2415
2316
  type: "module",
2416
2317
  packageManager: resolvePackageManagerVersion(options.packageManager),
2417
2318
  scripts: {
2418
- ...options.framework === "nuxt" ? { postinstall: "nuxt prepare" } : {},
2319
+ ...framework.scaffold.scripts,
2419
2320
  prepare: "holo key:generate && holo prepare",
2420
2321
  dev: "holo dev",
2421
2322
  build: "holo build",
2422
2323
  start: "holo start",
2423
- lint: options.framework === "nuxt" ? "eslint app config server shared tests *.d.ts --fix --no-warn-ignored --no-error-on-unmatched-pattern" : options.framework === "next" ? "eslint app config server tests --fix --no-warn-ignored --no-error-on-unmatched-pattern" : "eslint src config server tests --fix --no-warn-ignored --no-error-on-unmatched-pattern",
2424
- typecheck: options.framework === "nuxt" ? "nuxt typecheck" : options.framework === "next" ? "tsc -p tsconfig.json --noEmit" : "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
2324
+ lint: framework.scaffold.lintScript,
2325
+ typecheck: framework.scaffold.typecheckScript,
2425
2326
  ["config:cache"]: "holo config:cache",
2426
2327
  ["config:clear"]: "holo config:clear"
2427
2328
  },
@@ -2446,12 +2347,9 @@ ${normalizedGroup.join("\n")}
2446
2347
  `;
2447
2348
  }
2448
2349
  async function scaffoldProject(projectRoot, options) {
2449
- const existingEntries = await readdir(projectRoot).catch(() => []);
2450
- if (existingEntries.length > 0) {
2451
- throw new Error(`Refusing to scaffold into a non-empty directory: ${projectRoot}`);
2452
- }
2350
+ await ensureEmptyDirectory(projectRoot);
2453
2351
  const { env, example } = renderScaffoldEnvFiles(options);
2454
- const config = normalizeHoloProjectConfig();
2352
+ const config = normalizeHoloProjectConfig2();
2455
2353
  const generatedSchemaPath = resolveGeneratedSchemaPath(projectRoot, config);
2456
2354
  const optionalPackages = normalizeScaffoldOptionalPackages(options.optionalPackages);
2457
2355
  const storageEnabled = optionalPackages.includes("storage");
@@ -2505,6 +2403,7 @@ async function scaffoldProject(projectRoot, options) {
2505
2403
  }
2506
2404
  await writeFile(resolve3(projectRoot, "package.json"), renderScaffoldPackageJson(options), "utf8");
2507
2405
  await writeFile(resolve3(projectRoot, ".gitignore"), renderScaffoldGitignore(), "utf8");
2406
+ await writeFile(resolve3(projectRoot, "eslint.config.mjs"), renderScaffoldEslintConfig(), "utf8");
2508
2407
  await writeFile(resolve3(projectRoot, ".env"), scaffoldEnv, "utf8");
2509
2408
  await writeFile(resolve3(projectRoot, ".env.example"), scaffoldEnvExample, "utf8");
2510
2409
  await writeFile(resolve3(projectRoot, "config/app.ts"), renderScaffoldAppConfig(options.projectName, options.framework), "utf8");
@@ -2735,10 +2634,8 @@ async function syncHostedAuthRouteFiles(projectRoot, features) {
2735
2634
  await mkdir3(dirname2(targetPath), { recursive: true });
2736
2635
  await writeTextFile(targetPath, file.contents);
2737
2636
  }
2738
- if (framework === "next") {
2739
- for (const file of renderNextManagedHostedAuthRouteFiles(features)) {
2740
- await writeTextFile(resolve4(projectRoot, file.path), file.contents);
2741
- }
2637
+ for (const file of renderManagedHostedAuthRouteFiles(framework, features)) {
2638
+ await writeTextFile(resolve4(projectRoot, file.path), file.contents);
2742
2639
  }
2743
2640
  }
2744
2641
  async function syncAuthRouteFiles(projectRoot) {
@@ -3092,7 +2989,7 @@ async function installMediaIntoProject(projectRoot) {
3092
2989
  if (!mediaConfigPath) {
3093
2990
  await writeTextFile(resolve4(projectRoot, "config/media.ts"), renderMediaConfig());
3094
2991
  }
3095
- const { createMediaTableMigration } = await import("./media-migrations-BNVAPYEM.mjs");
2992
+ const { createMediaTableMigration } = await import("./media-migrations-UJ6BMUSZ.mjs");
3096
2993
  const migrationFilePath = await createMediaTableMigration(projectRoot, {
3097
2994
  skipIfExists: true
3098
2995
  });
@@ -3179,8 +3076,9 @@ async function installBroadcastIntoProject(projectRoot) {
3179
3076
  const broadcastConfigPath = await resolveFirstExistingPath(projectRoot, BROADCAST_CONFIG_FILE_NAMES);
3180
3077
  const authConfigPath = await resolveFirstExistingPath(projectRoot, AUTH_CONFIG_FILE_NAMES);
3181
3078
  const { dependencies, devDependencies } = await readPackageJsonDependencyState(projectRoot);
3182
- const framework = detectProjectFrameworkFromPackageJson(dependencies, devDependencies);
3183
- const canCreateBroadcastAuthRoute = framework === "next" || framework === "nuxt" || framework === "sveltekit";
3079
+ const framework = await detectProjectFrameworkDescriptor(projectRoot, dependencies, devDependencies);
3080
+ const frameworkId = framework && isSupportedFrameworkId(framework.id) ? framework.id : void 0;
3081
+ const canCreateBroadcastAuthRoute = frameworkDescriptorSupportsManagedBroadcastAuthRoute(framework);
3184
3082
  const broadcastRoot = resolve4(projectRoot, "server/broadcast");
3185
3083
  const channelsRoot = resolve4(projectRoot, "server/channels");
3186
3084
  const broadcastDirectoryExists = await pathExists(broadcastRoot);
@@ -3206,24 +3104,17 @@ async function installBroadcastIntoProject(projectRoot) {
3206
3104
  const nextEnvExample = upsertEnvContents(await readTextFile(envExamplePath), broadcastEnvFiles.example);
3207
3105
  const dependencyResult = await upsertBroadcastPackageDependencies(projectRoot);
3208
3106
  let createdFrameworkSetup = false;
3209
- if (framework === "next") {
3210
- const holoHelperPath = resolve4(projectRoot, ".holo-js/generated/next/holo.ts");
3211
- const broadcastConfigRoutePath = resolve4(projectRoot, "app/broadcasting/config/route.ts");
3212
- const generatedBroadcastConfigRoutePath = resolve4(projectRoot, ".holo-js/generated/next/broadcast-config-route.ts");
3213
- if (!await pathExists(holoHelperPath)) {
3214
- await writeTextFile(holoHelperPath, renderNextHoloHelper());
3215
- createdFrameworkSetup = true;
3216
- }
3217
- if (!await pathExists(broadcastConfigRoutePath)) {
3218
- await writeTextFile(broadcastConfigRoutePath, renderNextBroadcastConfigRoute());
3219
- createdFrameworkSetup = true;
3220
- }
3221
- await writeTextFile(generatedBroadcastConfigRoutePath, renderNextGeneratedBroadcastConfigRoute());
3222
- } else if (framework === "sveltekit") {
3223
- const holoHelperPath = resolve4(projectRoot, ".holo-js/generated/sveltekit/holo.ts");
3224
- if (!await pathExists(holoHelperPath)) {
3225
- await writeTextFile(holoHelperPath, renderSvelteHoloHelper());
3226
- createdFrameworkSetup = true;
3107
+ if (frameworkId) {
3108
+ for (const file of renderBroadcastInstallFrameworkFiles(frameworkId)) {
3109
+ const filePath = resolve4(projectRoot, file.path);
3110
+ const generated = file.path.startsWith(".holo-js/generated/");
3111
+ if (!await pathExists(filePath)) {
3112
+ createdFrameworkSetup = true;
3113
+ }
3114
+ if (!generated && await pathExists(filePath)) {
3115
+ continue;
3116
+ }
3117
+ await writeTextFile(filePath, file.contents);
3227
3118
  }
3228
3119
  }
3229
3120
  const broadcastAuthSupport = await syncBroadcastAuthSupportAfterAuthInstall(projectRoot);
@@ -3302,5 +3193,8 @@ export {
3302
3193
  installMediaIntoProject,
3303
3194
  installSecurityIntoProject,
3304
3195
  installCacheIntoProject,
3305
- installBroadcastIntoProject
3196
+ installBroadcastIntoProject,
3197
+ fileExists,
3198
+ ensureAbsent,
3199
+ ensureEmptyDirectory
3306
3200
  };