@holo-js/cli 0.1.4 → 0.1.5

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 (56) hide show
  1. package/dist/bin/holo.mjs +192 -35
  2. package/dist/{broadcast-CSSARTSA.mjs → broadcast-RT5KVZWP.mjs} +5 -5
  3. package/dist/{cache-4G6QGIZO.mjs → cache-NHCCHT44.mjs} +5 -5
  4. package/dist/{cache-migrations-NATT5WPQ.mjs → cache-migrations-R2RL2RVD.mjs} +15 -16
  5. package/dist/{chunk-EUIVXVJL.mjs → chunk-57SJ566R.mjs} +1 -1
  6. package/dist/chunk-5BLEC66P.mjs +284 -0
  7. package/dist/{chunk-JX2ZH6XY.mjs → chunk-5EU32E7X.mjs} +3 -3
  8. package/dist/{chunk-Q5F6C2D4.mjs → chunk-BAFQ2GOA.mjs} +1 -1
  9. package/dist/{chunk-CUL4RJTG.mjs → chunk-F4MT6GBK.mjs} +1 -1
  10. package/dist/{chunk-3OTCSFDG.mjs → chunk-MXKNQACM.mjs} +544 -82
  11. package/dist/{chunk-QYLSMF7V.mjs → chunk-OZUDZEAW.mjs} +142 -28
  12. package/dist/{chunk-66FHW725.mjs → chunk-R6BWRY3E.mjs} +28 -2
  13. package/dist/{chunk-ZLRO7HXY.mjs → chunk-SCCPDJGO.mjs} +156 -15
  14. package/dist/{chunk-VT5IDQG6.mjs → chunk-UZTDQKIY.mjs} +61 -44
  15. package/dist/{chunk-MZXN2YMI.mjs → chunk-VCEO6N5T.mjs} +3542 -2517
  16. package/dist/{config-LS5USBRB.mjs → config-5JSC6KJG.mjs} +3 -3
  17. package/dist/{dev-LZ3O2E3U.mjs → dev-OSLYSBTL.mjs} +7 -7
  18. package/dist/{discovery-GBLAUTXS.mjs → discovery-JLT2EOGH.mjs} +3 -3
  19. package/dist/{generators-DSN4GWJI.mjs → generators-ZIWACCBE.mjs} +134 -16
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.mjs +189 -32
  22. package/dist/media-migrations-UBAL2YVV.mjs +117 -0
  23. package/dist/{queue-FV35LLPR.mjs → queue-I66EISVS.mjs} +14 -14
  24. package/dist/{queue-migrations-SSIYKK5S.mjs → queue-migrations-UIAMAB6E.mjs} +24 -20
  25. package/dist/{runtime-EFZ5H5IL.mjs → runtime-MMQGO4PP.mjs} +9 -7
  26. package/dist/{runtime-OOSJ5JBY.mjs → runtime-ZKD6URAV.mjs} +1 -1
  27. package/dist/{scaffold-7OTDH4UR.mjs → scaffold-ISDVICNQ.mjs} +18 -5
  28. package/dist/{security-ATKDC26E.mjs → security-OZXTMYXF.mjs} +10 -7
  29. package/package.json +13 -12
  30. package/dist/broadcast-YSIJCL3R.mjs +0 -85
  31. package/dist/cache-OWQY4E7W.mjs +0 -67
  32. package/dist/cache-migrations-RVEA6CEU.mjs +0 -155
  33. package/dist/chunk-BWW5TDFI.mjs +0 -4
  34. package/dist/chunk-D4GG556Y.mjs +0 -23
  35. package/dist/chunk-DMH2B4UQ.mjs +0 -343
  36. package/dist/chunk-ET7UXHHQ.mjs +0 -166
  37. package/dist/chunk-G5ADO27Q.mjs +0 -463
  38. package/dist/chunk-GSQ3HTRO.mjs +0 -165
  39. package/dist/chunk-H7TJ4FB3.mjs +0 -848
  40. package/dist/chunk-ICJR7TS4.mjs +0 -66
  41. package/dist/chunk-M7J3YTHR.mjs +0 -26
  42. package/dist/chunk-QFUSWV3J.mjs +0 -3237
  43. package/dist/chunk-S7P7EBM3.mjs +0 -787
  44. package/dist/chunk-SRWJU3A5.mjs +0 -11
  45. package/dist/chunk-URK7C3VQ.mjs +0 -538
  46. package/dist/chunk-XUYKPU5Q.mjs +0 -272
  47. package/dist/config-DMWBMMGD.mjs +0 -26
  48. package/dist/dev-KQFT7RHR.mjs +0 -43
  49. package/dist/discovery-R733D2PO.mjs +0 -29
  50. package/dist/generators-WX45BI4U.mjs +0 -426
  51. package/dist/queue-6OG7VJ34.mjs +0 -626
  52. package/dist/queue-migrations-NK2EYX3J.mjs +0 -163
  53. package/dist/runtime-4BV3JODY.mjs +0 -56
  54. package/dist/runtime-ANBO7VQM.mjs +0 -33
  55. package/dist/scaffold-DRKBGS2K.mjs +0 -120
  56. package/dist/security-R7VH6W5H.mjs +0 -69
package/dist/index.mjs CHANGED
@@ -14,24 +14,24 @@ import {
14
14
  resolveNewProjectInput,
15
15
  resolveStringFlag,
16
16
  splitCsv
17
- } from "./chunk-JX2ZH6XY.mjs";
17
+ } from "./chunk-5EU32E7X.mjs";
18
+ import {
19
+ hasProjectDependency
20
+ } from "./chunk-F4MT6GBK.mjs";
18
21
  import {
19
22
  writeLine
20
23
  } from "./chunk-ZXDU7RHU.mjs";
21
24
  import {
22
25
  fileExists
23
- } from "./chunk-EUIVXVJL.mjs";
24
- import {
25
- hasProjectDependency
26
- } from "./chunk-CUL4RJTG.mjs";
26
+ } from "./chunk-57SJ566R.mjs";
27
27
  import "./chunk-D7O4SU6N.mjs";
28
- import "./chunk-S7P7EBM3.mjs";
29
- import "./chunk-MZXN2YMI.mjs";
30
- import "./chunk-GSQ3HTRO.mjs";
31
- import "./chunk-H7TJ4FB3.mjs";
28
+ import "./chunk-UZTDQKIY.mjs";
29
+ import "./chunk-VCEO6N5T.mjs";
30
+ import "./chunk-5BLEC66P.mjs";
31
+ import "./chunk-MXKNQACM.mjs";
32
32
  import {
33
33
  SUPPORTED_AUTH_SOCIAL_PROVIDERS
34
- } from "./chunk-G5ADO27Q.mjs";
34
+ } from "./chunk-R6BWRY3E.mjs";
35
35
 
36
36
  // src/types.ts
37
37
  function defineCommand(command) {
@@ -39,13 +39,82 @@ function defineCommand(command) {
39
39
  }
40
40
 
41
41
  // src/cli.ts
42
- import { resolve } from "path";
42
+ import { resolve as resolve2 } from "path";
43
43
  import { clearConfigCache, resolveConfigCachePath } from "@holo-js/config";
44
+
45
+ // src/app-key.ts
46
+ import { randomBytes } from "crypto";
47
+ import { readFile, writeFile } from "fs/promises";
48
+ import { resolve } from "path";
49
+ var appKeyLinePattern = /^(\s*(?:export\s+)?APP_KEY\s*=\s*)(.*)$/;
50
+ function generateAppKey() {
51
+ return randomBytes(32).toString("base64");
52
+ }
53
+ function isEmptyAppKeyValue(value) {
54
+ const normalized = value.trim();
55
+ return normalized === "" || normalized === '""' || normalized === "''";
56
+ }
57
+ function renderEnvWithAppKey(existingContents, appKey) {
58
+ if (!existingContents) {
59
+ return {
60
+ contents: `APP_KEY=${appKey}
61
+ `,
62
+ changed: true
63
+ };
64
+ }
65
+ const lines = existingContents.replace(/\r\n/g, "\n").split("\n");
66
+ const appKeyIndex = lines.findIndex((line) => appKeyLinePattern.test(line));
67
+ if (appKeyIndex === -1) {
68
+ return {
69
+ contents: `${existingContents.replace(/\r\n/g, "\n").replace(/\n*$/, "")}
70
+ APP_KEY=${appKey}
71
+ `,
72
+ changed: true
73
+ };
74
+ }
75
+ const appKeyLine = lines[appKeyIndex];
76
+ const match = appKeyLine.match(appKeyLinePattern);
77
+ const prefix = match[1];
78
+ const value = match[2];
79
+ if (!isEmptyAppKeyValue(value)) {
80
+ return {
81
+ contents: existingContents,
82
+ changed: false
83
+ };
84
+ }
85
+ lines[appKeyIndex] = `${prefix}${appKey}`;
86
+ return {
87
+ contents: `${lines.join("\n").replace(/\n*$/, "")}
88
+ `,
89
+ changed: true
90
+ };
91
+ }
92
+ async function generateProjectAppKey(projectRoot) {
93
+ const envPath = resolve(projectRoot, ".env");
94
+ const existingContents = await readFile(envPath, "utf8").catch(() => void 0);
95
+ const key = generateAppKey();
96
+ const nextEnv = renderEnvWithAppKey(existingContents, key);
97
+ if (!nextEnv.changed || typeof nextEnv.contents !== "string") {
98
+ return {
99
+ envPath,
100
+ generated: false
101
+ };
102
+ }
103
+ await writeFile(envPath, nextEnv.contents, "utf8");
104
+ return {
105
+ envPath,
106
+ generated: true,
107
+ key
108
+ };
109
+ }
110
+
111
+ // src/cli.ts
44
112
  var runtimeModulePromise;
45
113
  var queueModulePromise;
46
114
  var cacheModulePromise;
47
115
  var queueMigrationsModulePromise;
48
116
  var cacheMigrationsModulePromise;
117
+ var mediaMigrationsModulePromise;
49
118
  var generatorsModulePromise;
50
119
  var broadcastModulePromise;
51
120
  var securityModulePromise;
@@ -55,55 +124,59 @@ var projectDiscoveryModulePromise;
55
124
  var projectRuntimeModulePromise;
56
125
  var projectScaffoldModulePromise;
57
126
  function loadRuntimeModule() {
58
- runtimeModulePromise ??= import("./runtime-EFZ5H5IL.mjs");
127
+ runtimeModulePromise ??= import("./runtime-MMQGO4PP.mjs");
59
128
  return runtimeModulePromise;
60
129
  }
61
130
  function loadQueueModule() {
62
- queueModulePromise ??= import("./queue-FV35LLPR.mjs");
131
+ queueModulePromise ??= import("./queue-I66EISVS.mjs");
63
132
  return queueModulePromise;
64
133
  }
65
134
  function loadCacheModule() {
66
- cacheModulePromise ??= import("./cache-4G6QGIZO.mjs");
135
+ cacheModulePromise ??= import("./cache-NHCCHT44.mjs");
67
136
  return cacheModulePromise;
68
137
  }
69
138
  function loadQueueMigrationsModule() {
70
- queueMigrationsModulePromise ??= import("./queue-migrations-SSIYKK5S.mjs");
139
+ queueMigrationsModulePromise ??= import("./queue-migrations-UIAMAB6E.mjs");
71
140
  return queueMigrationsModulePromise;
72
141
  }
73
142
  function loadCacheMigrationsModule() {
74
- cacheMigrationsModulePromise ??= import("./cache-migrations-NATT5WPQ.mjs");
143
+ cacheMigrationsModulePromise ??= import("./cache-migrations-R2RL2RVD.mjs");
75
144
  return cacheMigrationsModulePromise;
76
145
  }
146
+ function loadMediaMigrationsModule() {
147
+ mediaMigrationsModulePromise ??= import("./media-migrations-UBAL2YVV.mjs");
148
+ return mediaMigrationsModulePromise;
149
+ }
77
150
  function loadGeneratorsModule() {
78
- generatorsModulePromise ??= import("./generators-DSN4GWJI.mjs");
151
+ generatorsModulePromise ??= import("./generators-ZIWACCBE.mjs");
79
152
  return generatorsModulePromise;
80
153
  }
81
154
  function loadBroadcastModule() {
82
- broadcastModulePromise ??= import("./broadcast-CSSARTSA.mjs");
155
+ broadcastModulePromise ??= import("./broadcast-RT5KVZWP.mjs");
83
156
  return broadcastModulePromise;
84
157
  }
85
158
  function loadSecurityModule() {
86
- securityModulePromise ??= import("./security-ATKDC26E.mjs");
159
+ securityModulePromise ??= import("./security-OZXTMYXF.mjs");
87
160
  return securityModulePromise;
88
161
  }
89
162
  function loadDevModule() {
90
- devModulePromise ??= import("./dev-LZ3O2E3U.mjs");
163
+ devModulePromise ??= import("./dev-OSLYSBTL.mjs");
91
164
  return devModulePromise;
92
165
  }
93
166
  function loadProjectConfigModule() {
94
- projectConfigModulePromise ??= import("./config-LS5USBRB.mjs");
167
+ projectConfigModulePromise ??= import("./config-5JSC6KJG.mjs");
95
168
  return projectConfigModulePromise;
96
169
  }
97
170
  function loadProjectDiscoveryModule() {
98
- projectDiscoveryModulePromise ??= import("./discovery-GBLAUTXS.mjs");
171
+ projectDiscoveryModulePromise ??= import("./discovery-JLT2EOGH.mjs");
99
172
  return projectDiscoveryModulePromise;
100
173
  }
101
174
  function loadProjectRuntimeModule() {
102
- projectRuntimeModulePromise ??= import("./runtime-OOSJ5JBY.mjs");
175
+ projectRuntimeModulePromise ??= import("./runtime-ZKD6URAV.mjs");
103
176
  return projectRuntimeModulePromise;
104
177
  }
105
178
  function loadProjectScaffoldModule() {
106
- projectScaffoldModulePromise ??= import("./scaffold-DRKBGS2K.mjs");
179
+ projectScaffoldModulePromise ??= import("./scaffold-ISDVICNQ.mjs");
107
180
  return projectScaffoldModulePromise;
108
181
  }
109
182
  async function resolveRuntimeExecutor(runtimeExecutor) {
@@ -138,6 +211,9 @@ var cacheExecutorLoaders = {
138
211
  runCacheClearCommand: async () => (await loadCacheModule()).runCacheClearCommand,
139
212
  runCacheForgetCommand: async () => (await loadCacheModule()).runCacheForgetCommand
140
213
  };
214
+ var mediaExecutorLoaders = {
215
+ runMediaTableCommand: async () => (await loadMediaMigrationsModule()).runMediaTableCommand
216
+ };
141
217
  var broadcastExecutorLoaders = {
142
218
  runBroadcastWorkCommand: async () => (await loadBroadcastModule()).runBroadcastWorkCommand
143
219
  };
@@ -155,6 +231,13 @@ async function resolveCacheExecutor(cacheExecutors, key) {
155
231
  }
156
232
  return cacheExecutorLoaders[key]();
157
233
  }
234
+ async function resolveMediaExecutor(mediaExecutors, key) {
235
+ const existing = mediaExecutors[key];
236
+ if (existing) {
237
+ return existing;
238
+ }
239
+ return mediaExecutorLoaders[key]();
240
+ }
158
241
  async function resolveBroadcastExecutor(broadcastExecutors, key) {
159
242
  const existing = broadcastExecutors[key];
160
243
  if (existing) {
@@ -219,7 +302,7 @@ function resolvePackageManagerDevCommand(packageManager) {
219
302
  return "yarn dev";
220
303
  }
221
304
  }
222
- function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, projectExecutors = {}, broadcastExecutors = {}, securityExecutors = {}, cacheExecutors = {}) {
305
+ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, projectExecutors = {}, broadcastExecutors = {}, securityExecutors = {}, cacheExecutors = {}, mediaExecutors = {}) {
223
306
  return [
224
307
  {
225
308
  name: "list",
@@ -260,7 +343,7 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
260
343
  const optionalPackages = normalizeOptionalPackages(
261
344
  (collectMultiStringFlag(commandContext.flags, "package") ?? []).flatMap((entry) => splitCsv(entry) ?? [])
262
345
  );
263
- const targetDir = resolve(commandContext.cwd, projectName);
346
+ const targetDir = resolve2(commandContext.cwd, projectName);
264
347
  const { scaffoldProject } = await loadProjectScaffoldModule();
265
348
  await scaffoldProject(targetDir, {
266
349
  projectName,
@@ -278,10 +361,26 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
278
361
  writeLine(context.stdout, ` ${resolvePackageManagerDevCommand(packageManager)}`);
279
362
  }
280
363
  },
364
+ {
365
+ name: "key:generate",
366
+ description: "Generate APP_KEY in the project .env file when it is missing.",
367
+ usage: "holo key:generate",
368
+ source: "internal",
369
+ async prepare() {
370
+ return { args: [], flags: {} };
371
+ },
372
+ async run() {
373
+ const result = await generateProjectAppKey(context.projectRoot);
374
+ writeLine(
375
+ context.stdout,
376
+ result.generated ? `Generated APP_KEY in ${result.envPath}.` : `APP_KEY is already set in ${result.envPath}.`
377
+ );
378
+ }
379
+ },
281
380
  {
282
381
  name: "install",
283
382
  description: "Install first-party Holo support into an existing project.",
284
- usage: "holo install <queue|events|auth|authorization|notifications|mail|broadcast|security|cache> [--driver <queue: sync|file|redis|database; cache: file|redis|database; security: file|redis>] [--social] [--provider <google|github|discord|facebook|apple|linkedin>] [--workos] [--clerk]",
383
+ usage: "holo install <queue|events|auth|authorization|notifications|mail|broadcast|security|cache|media> [--driver <queue: sync|file|redis|database; cache: file|redis|database>] [--social] [--provider <google|github|discord|facebook|apple|linkedin>] [--workos] [--clerk]",
285
384
  source: "internal",
286
385
  async prepare(input) {
287
386
  const target = normalizeChoice(
@@ -311,6 +410,9 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
311
410
  if (target === "security" && requestedDriver) {
312
411
  throw new Error("The security installer does not support --driver.");
313
412
  }
413
+ if (target === "media" && requestedDriver) {
414
+ throw new Error("The media installer does not support --driver.");
415
+ }
314
416
  const driver = target === "queue" ? requestedDriver ? normalizeChoice(requestedDriver, SUPPORTED_QUEUE_INSTALL_DRIVERS, "queue driver") : "sync" : target === "cache" ? requestedDriver ? normalizeChoice(requestedDriver, SUPPORTED_CACHE_INSTALL_DRIVERS, "cache driver") : "file" : void 0;
315
417
  const socialProviders = target === "auth" ? (collectMultiStringFlag(input.flags, "provider") ?? []).flatMap((entry) => splitCsv(entry) ?? []).map((provider) => normalizeChoice(provider, SUPPORTED_AUTH_SOCIAL_PROVIDERS, "auth social provider")) : [];
316
418
  const social = target === "auth" ? resolveBooleanFlag(input.flags, "social") === true || socialProviders.length > 0 : false;
@@ -335,9 +437,9 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
335
437
  installEventsIntoProject,
336
438
  installQueueIntoProject: installQueueIntoProject2
337
439
  } = await loadProjectScaffoldModule();
440
+ const queueConfigured = await hasProjectDependency(context.projectRoot, "@holo-js/queue") || await fileExists(resolve2(context.projectRoot, "config/queue.ts")) || await fileExists(resolve2(context.projectRoot, "config/queue.mts")) || await fileExists(resolve2(context.projectRoot, "config/queue.js")) || await fileExists(resolve2(context.projectRoot, "config/queue.mjs"));
338
441
  const eventsResult = await installEventsIntoProject(context.projectRoot);
339
442
  let queueResult;
340
- const queueConfigured = await hasProjectDependency(context.projectRoot, "@holo-js/queue") || await fileExists(resolve(context.projectRoot, "config/queue.ts")) || await fileExists(resolve(context.projectRoot, "config/queue.mts")) || await fileExists(resolve(context.projectRoot, "config/queue.js")) || await fileExists(resolve(context.projectRoot, "config/queue.mjs"));
341
443
  if (!queueConfigured && isInteractive(context, commandContext.flags)) {
342
444
  const enableQueuedListeners = await confirm(
343
445
  context,
@@ -371,11 +473,13 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
371
473
  workos: commandContext.flags.workos === true,
372
474
  clerk: commandContext.flags.clerk === true
373
475
  });
374
- const changed2 = result2.updatedPackageJson || result2.createdAuthConfig || result2.createdSessionConfig || result2.createdUserModel || result2.createdMigrationFiles.length > 0 || result2.updatedEnv || result2.updatedEnvExample;
476
+ const changed2 = result2.updatedPackageJson || result2.createdAuthConfig || result2.createdSessionConfig || result2.createdSecurityConfig || result2.createdCorsConfig || result2.createdUserModel || result2.createdMigrationFiles.length > 0 || result2.updatedEnv || result2.updatedEnvExample;
375
477
  writeLine(context.stdout, changed2 ? "Installed auth support." : "Auth support is already installed.");
376
478
  if (result2.updatedPackageJson) writeLine(context.stdout, " - updated package.json");
377
479
  if (result2.createdAuthConfig) writeLine(context.stdout, " - created config/auth.ts");
378
480
  if (result2.createdSessionConfig) writeLine(context.stdout, " - created config/session.ts");
481
+ if (result2.createdSecurityConfig) writeLine(context.stdout, " - created config/security.ts");
482
+ if (result2.createdCorsConfig) writeLine(context.stdout, " - created config/cors.ts");
379
483
  if (result2.createdUserModel) writeLine(context.stdout, " - created server/models/User.ts");
380
484
  if (result2.updatedEnv) writeLine(context.stdout, " - updated .env");
381
485
  if (result2.updatedEnvExample) writeLine(context.stdout, " - updated .env.example");
@@ -409,9 +513,11 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
409
513
  if (target === "mail") {
410
514
  const { installMailIntoProject } = await loadProjectScaffoldModule();
411
515
  const result2 = await installMailIntoProject(context.projectRoot);
412
- const changed2 = result2.updatedPackageJson || result2.createdMailConfig || result2.createdMailDirectory;
516
+ const changed2 = result2.updatedPackageJson || result2.createdMailConfig || result2.createdMailDirectory || result2.updatedEnv || result2.updatedEnvExample;
413
517
  writeLine(context.stdout, changed2 ? "Installed mail support." : "Mail support is already installed.");
414
518
  if (result2.updatedPackageJson) writeLine(context.stdout, " - updated package.json");
519
+ if (result2.updatedEnv) writeLine(context.stdout, " - updated .env");
520
+ if (result2.updatedEnvExample) writeLine(context.stdout, " - updated .env.example");
415
521
  if (result2.createdMailConfig) writeLine(context.stdout, " - created config/mail.ts");
416
522
  if (result2.createdMailDirectory) writeLine(context.stdout, " - created server/mail");
417
523
  return;
@@ -434,10 +540,11 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
434
540
  if (target === "security") {
435
541
  const { installSecurityIntoProject } = await loadProjectScaffoldModule();
436
542
  const result2 = await installSecurityIntoProject(context.projectRoot);
437
- const changed2 = result2.updatedPackageJson || result2.createdSecurityConfig;
543
+ const changed2 = result2.updatedPackageJson || result2.createdSecurityConfig || result2.createdCorsConfig;
438
544
  writeLine(context.stdout, changed2 ? "Installed security support." : "Security support is already installed.");
439
545
  if (result2.updatedPackageJson) writeLine(context.stdout, " - updated package.json");
440
546
  if (result2.createdSecurityConfig) writeLine(context.stdout, " - created config/security.ts");
547
+ if (result2.createdCorsConfig) writeLine(context.stdout, " - created config/cors.ts");
441
548
  return;
442
549
  }
443
550
  if (target === "cache") {
@@ -455,6 +562,16 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
455
562
  if (result2.databaseDriver) writeLine(context.stdout, ' - run "holo cache:table" to create the cache tables');
456
563
  return;
457
564
  }
565
+ if (target === "media") {
566
+ const { installMediaIntoProject } = await loadProjectScaffoldModule();
567
+ const result2 = await installMediaIntoProject(context.projectRoot);
568
+ const changed2 = result2.createdMediaConfig || result2.updatedPackageJson || result2.createdMigrationFiles.length > 0;
569
+ writeLine(context.stdout, changed2 ? "Installed media support." : "Media support is already installed.");
570
+ if (result2.createdMediaConfig) writeLine(context.stdout, " - created config/media.ts");
571
+ if (result2.updatedPackageJson) writeLine(context.stdout, " - updated package.json");
572
+ if (result2.createdMigrationFiles.length > 0) writeLine(context.stdout, " - created media migration");
573
+ return;
574
+ }
458
575
  if (target !== "queue") {
459
576
  throw new Error(`Unsupported install target: ${target || "(empty)"}.`);
460
577
  }
@@ -471,6 +588,33 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
471
588
  if (result.createdJobsDirectory) writeLine(context.stdout, " - created server/jobs");
472
589
  }
473
590
  },
591
+ {
592
+ name: "auth:notifications:publish",
593
+ description: "Publish editable auth notification definitions into the application.",
594
+ usage: "holo auth:notifications:publish",
595
+ source: "internal",
596
+ async prepare() {
597
+ return { args: [], flags: {} };
598
+ },
599
+ async run() {
600
+ const { publishAuthNotificationsIntoProject } = await loadProjectScaffoldModule();
601
+ const result = await publishAuthNotificationsIntoProject(context.projectRoot);
602
+ const changed = result.createdFiles.length > 0;
603
+ writeLine(context.stdout, changed ? "Published auth notification files." : "Auth notification files are already published.");
604
+ for (const filePath of result.createdFiles) {
605
+ writeLine(context.stdout, ` - created ${filePath}`);
606
+ }
607
+ for (const filePath of result.skippedFiles) {
608
+ writeLine(context.stdout, ` - skipped existing ${filePath}`);
609
+ }
610
+ if (!result.hasMailDependency) {
611
+ writeLine(
612
+ context.stdout,
613
+ " - note: install @holo-js/mail or configure a notification mailer before email delivery can send."
614
+ );
615
+ }
616
+ }
617
+ },
474
618
  {
475
619
  name: "prepare",
476
620
  description: "Discover Holo resources and refresh generated registries.",
@@ -587,6 +731,19 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
587
731
  );
588
732
  }
589
733
  },
734
+ {
735
+ name: "media:table",
736
+ description: "Generate the media table migration.",
737
+ usage: "holo media:table",
738
+ source: "internal",
739
+ async prepare() {
740
+ return { args: [], flags: {} };
741
+ },
742
+ async run() {
743
+ const runMediaTableCommand = await resolveMediaExecutor(mediaExecutors, "runMediaTableCommand");
744
+ await runMediaTableCommand(context, context.projectRoot);
745
+ }
746
+ },
590
747
  {
591
748
  name: "queue:table",
592
749
  description: "Generate the database queue jobs table migration.",
@@ -1323,7 +1480,7 @@ async function runCli(argv, io) {
1323
1480
  };
1324
1481
  const internalCommands = createInternalCommands(internalContext);
1325
1482
  const registry = [...internalCommands];
1326
- const canSkipAppDiscovery = requestedCommandName === "config:cache" || requestedCommandName === "config:clear" || requestedCommandName === "new" || requestedCommandName === "install" || requestedCommandName === "prepare" || requestedCommandName === "dev" || requestedCommandName === "build" || requestedCommandName === "cache:table" || requestedCommandName === "cache:clear" || requestedCommandName === "cache:forget" || requestedCommandName === "broadcast:work" || requestedCommandName === "queue:table" || requestedCommandName === "queue:failed-table" || requestedCommandName === "queue:work" || requestedCommandName === "queue:listen" || requestedCommandName === "queue:failed" || requestedCommandName === "queue:retry" || requestedCommandName === "queue:forget" || requestedCommandName === "queue:flush" || requestedCommandName === "queue:restart" || requestedCommandName === "queue:clear" || requestedCommandName === "rate-limit:clear";
1483
+ const canSkipAppDiscovery = requestedCommandName === "config:cache" || requestedCommandName === "config:clear" || requestedCommandName === "key:generate" || requestedCommandName === "new" || requestedCommandName === "install" || requestedCommandName === "auth:notifications:publish" || requestedCommandName === "prepare" || requestedCommandName === "dev" || requestedCommandName === "build" || requestedCommandName === "cache:table" || requestedCommandName === "cache:clear" || requestedCommandName === "cache:forget" || requestedCommandName === "media:table" || requestedCommandName === "broadcast:work" || requestedCommandName === "queue:table" || requestedCommandName === "queue:failed-table" || requestedCommandName === "queue:work" || requestedCommandName === "queue:listen" || requestedCommandName === "queue:failed" || requestedCommandName === "queue:retry" || requestedCommandName === "queue:forget" || requestedCommandName === "queue:flush" || requestedCommandName === "queue:restart" || requestedCommandName === "queue:clear" || requestedCommandName === "rate-limit:clear";
1327
1484
  if (!canSkipAppDiscovery) {
1328
1485
  const initialProject = await loadProject();
1329
1486
  const appCommands = (await (await loadProjectDiscoveryModule()).discoverAppCommands(projectRoot, initialProject.config)).map((entry) => createAppCommandDefinition(entry));
@@ -0,0 +1,117 @@
1
+ import {
2
+ getRegistryMigrationSlug,
3
+ hasRegisteredCreateTableMigration,
4
+ hasRegisteredMigrationSlug,
5
+ nextMigrationTemplate
6
+ } from "./chunk-BAFQ2GOA.mjs";
7
+ import {
8
+ writeLine
9
+ } from "./chunk-ZXDU7RHU.mjs";
10
+ import "./chunk-57SJ566R.mjs";
11
+ import "./chunk-D7O4SU6N.mjs";
12
+ import {
13
+ prepareProjectDiscovery
14
+ } from "./chunk-UZTDQKIY.mjs";
15
+ import "./chunk-VCEO6N5T.mjs";
16
+ import {
17
+ ensureProjectConfig
18
+ } from "./chunk-5BLEC66P.mjs";
19
+ import {
20
+ loadGeneratedProjectRegistry
21
+ } from "./chunk-MXKNQACM.mjs";
22
+ import {
23
+ makeProjectRelativePath,
24
+ resolveDefaultArtifactPath,
25
+ writeTextFile
26
+ } from "./chunk-R6BWRY3E.mjs";
27
+
28
+ // src/media-migrations.ts
29
+ import { readdir } from "fs/promises";
30
+ import { resolve } from "path";
31
+ import { normalizeMigrationSlug } from "@holo-js/db";
32
+ var DEFAULT_MEDIA_TABLE = "media";
33
+ function normalizeMediaMigrationName(tableName = DEFAULT_MEDIA_TABLE) {
34
+ return normalizeMigrationSlug(`create_${tableName.replaceAll(".", "_")}_table`);
35
+ }
36
+ function renderMediaTableMigration(tableName = DEFAULT_MEDIA_TABLE) {
37
+ const tableNameLiteral = JSON.stringify(tableName);
38
+ return [
39
+ "import { defineMigration, type MigrationContext } from '@holo-js/db'",
40
+ "",
41
+ "export default defineMigration({",
42
+ " async up({ schema }: MigrationContext) {",
43
+ ` await schema.createTable(${tableNameLiteral}, (table) => {`,
44
+ " table.id()",
45
+ " table.uuid('uuid').unique()",
46
+ " table.string('model_type')",
47
+ " table.string('model_id')",
48
+ " table.string('collection_name').default('default')",
49
+ " table.string('name')",
50
+ " table.string('file_name')",
51
+ " table.string('disk')",
52
+ " table.string('conversions_disk').nullable()",
53
+ " table.string('mime_type').nullable()",
54
+ " table.string('extension').nullable()",
55
+ " table.bigInteger('size')",
56
+ " table.string('path')",
57
+ " table.json('generated_conversions').default({})",
58
+ " table.integer('order_column').default(1)",
59
+ " table.timestamps()",
60
+ " table.index(['model_type', 'model_id'])",
61
+ " table.index(['model_type', 'model_id', 'collection_name'])",
62
+ " })",
63
+ " },",
64
+ " async down({ schema }: MigrationContext) {",
65
+ ` await schema.dropTable(${tableNameLiteral})`,
66
+ " },",
67
+ "})",
68
+ ""
69
+ ].join("\n");
70
+ }
71
+ async function hasMigrationFile(migrationsDir, migrationName) {
72
+ const entries = await readdir(migrationsDir).catch(() => []);
73
+ return entries.some((entry) => entry.endsWith(`_${migrationName}.ts`) || entry.endsWith(`_${migrationName}.mts`) || entry.endsWith(`_${migrationName}.js`) || entry.endsWith(`_${migrationName}.mjs`) || entry.endsWith(`_${migrationName}.cts`) || entry.endsWith(`_${migrationName}.cjs`));
74
+ }
75
+ async function createMediaTableMigration(projectRoot, options = {}) {
76
+ const project = await ensureProjectConfig(projectRoot);
77
+ const registry = await loadGeneratedProjectRegistry(projectRoot) ?? await prepareProjectDiscovery(projectRoot, project.config);
78
+ const tableName = DEFAULT_MEDIA_TABLE;
79
+ const migrationName = normalizeMediaMigrationName(tableName);
80
+ const migrationsDir = resolve(projectRoot, project.config.paths.migrations);
81
+ if (hasRegisteredMigrationSlug(registry, migrationName) || hasRegisteredCreateTableMigration(registry, tableName) || await hasMigrationFile(migrationsDir, migrationName)) {
82
+ if (options.skipIfExists === true) {
83
+ return void 0;
84
+ }
85
+ throw new Error(`A migration for table "${tableName}" already exists.`);
86
+ }
87
+ const migrationTemplate = await nextMigrationTemplate(
88
+ migrationName,
89
+ migrationsDir
90
+ );
91
+ const migrationFilePath = resolveDefaultArtifactPath(projectRoot, project.config.paths.migrations, migrationTemplate.fileName);
92
+ await writeTextFile(migrationFilePath, renderMediaTableMigration(tableName));
93
+ return migrationFilePath;
94
+ }
95
+ async function runMediaTableCommand(io, projectRoot) {
96
+ const migrationFilePath = await createMediaTableMigration(projectRoot);
97
+ const { runProjectPrepare } = await import("./dev-OSLYSBTL.mjs");
98
+ if (!migrationFilePath) {
99
+ throw new Error(`A migration for table "${DEFAULT_MEDIA_TABLE}" already exists.`);
100
+ }
101
+ await runProjectPrepare(projectRoot);
102
+ writeLine(io.stdout, `Created migration: ${makeProjectRelativePath(projectRoot, migrationFilePath)}`);
103
+ }
104
+ var mediaMigrationInternals = {
105
+ getRegistryMigrationSlug,
106
+ hasRegisteredMigrationSlug,
107
+ hasRegisteredCreateTableMigration,
108
+ nextMigrationTemplate
109
+ };
110
+ export {
111
+ DEFAULT_MEDIA_TABLE,
112
+ createMediaTableMigration,
113
+ mediaMigrationInternals,
114
+ normalizeMediaMigrationName,
115
+ renderMediaTableMigration,
116
+ runMediaTableCommand
117
+ };
@@ -1,35 +1,35 @@
1
- import {
2
- writeLine
3
- } from "./chunk-ZXDU7RHU.mjs";
4
- import {
5
- initializeProjectRuntime
6
- } from "./chunk-URK7C3VQ.mjs";
7
- import "./chunk-EUIVXVJL.mjs";
8
1
  import {
9
2
  isIgnorableWatchError,
10
3
  isRecursiveWatchUnsupported,
11
4
  normalizeWatchedFilePath,
12
5
  runProjectPrepare,
13
6
  toPosixSlashes
14
- } from "./chunk-ZLRO7HXY.mjs";
15
- import "./chunk-CUL4RJTG.mjs";
7
+ } from "./chunk-SCCPDJGO.mjs";
8
+ import "./chunk-F4MT6GBK.mjs";
9
+ import {
10
+ writeLine
11
+ } from "./chunk-ZXDU7RHU.mjs";
12
+ import {
13
+ initializeProjectRuntime
14
+ } from "./chunk-OZUDZEAW.mjs";
15
+ import "./chunk-57SJ566R.mjs";
16
16
  import "./chunk-D7O4SU6N.mjs";
17
17
  import {
18
18
  prepareProjectDiscovery
19
- } from "./chunk-S7P7EBM3.mjs";
20
- import "./chunk-MZXN2YMI.mjs";
19
+ } from "./chunk-UZTDQKIY.mjs";
20
+ import "./chunk-VCEO6N5T.mjs";
21
21
  import {
22
22
  ensureProjectConfig,
23
23
  loadProjectConfig
24
- } from "./chunk-GSQ3HTRO.mjs";
24
+ } from "./chunk-5BLEC66P.mjs";
25
25
  import {
26
26
  loadGeneratedProjectRegistry
27
- } from "./chunk-H7TJ4FB3.mjs";
27
+ } from "./chunk-MXKNQACM.mjs";
28
28
  import {
29
29
  HOLO_RUNTIME_ROOT,
30
30
  bundleProjectModule,
31
31
  resolveProjectPackageImportSpecifier
32
- } from "./chunk-G5ADO27Q.mjs";
32
+ } from "./chunk-R6BWRY3E.mjs";
33
33
 
34
34
  // src/queue.ts
35
35
  import { spawn } from "child_process";