@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/bin/holo.mjs CHANGED
@@ -15,33 +15,102 @@ import {
15
15
  resolveNewProjectInput,
16
16
  resolveStringFlag,
17
17
  splitCsv
18
- } from "../chunk-XUYKPU5Q.mjs";
18
+ } from "../chunk-5EU32E7X.mjs";
19
+ import {
20
+ hasProjectDependency
21
+ } from "../chunk-F4MT6GBK.mjs";
19
22
  import {
20
23
  writeLine
21
- } from "../chunk-SRWJU3A5.mjs";
24
+ } from "../chunk-ZXDU7RHU.mjs";
22
25
  import {
23
26
  fileExists
24
- } from "../chunk-M7J3YTHR.mjs";
25
- import {
26
- hasProjectDependency
27
- } from "../chunk-D4GG556Y.mjs";
28
- import "../chunk-BWW5TDFI.mjs";
29
- import "../chunk-VT5IDQG6.mjs";
30
- import "../chunk-QFUSWV3J.mjs";
31
- import "../chunk-ET7UXHHQ.mjs";
32
- import "../chunk-3OTCSFDG.mjs";
27
+ } from "../chunk-57SJ566R.mjs";
28
+ import "../chunk-D7O4SU6N.mjs";
29
+ import "../chunk-UZTDQKIY.mjs";
30
+ import "../chunk-VCEO6N5T.mjs";
31
+ import "../chunk-5BLEC66P.mjs";
32
+ import "../chunk-MXKNQACM.mjs";
33
33
  import {
34
34
  SUPPORTED_AUTH_SOCIAL_PROVIDERS
35
- } from "../chunk-66FHW725.mjs";
35
+ } from "../chunk-R6BWRY3E.mjs";
36
36
 
37
37
  // src/cli.ts
38
- import { resolve } from "path";
38
+ import { resolve as resolve2 } from "path";
39
39
  import { clearConfigCache, resolveConfigCachePath } from "@holo-js/config";
40
+
41
+ // src/app-key.ts
42
+ import { randomBytes } from "crypto";
43
+ import { readFile, writeFile } from "fs/promises";
44
+ import { resolve } from "path";
45
+ var appKeyLinePattern = /^(\s*(?:export\s+)?APP_KEY\s*=\s*)(.*)$/;
46
+ function generateAppKey() {
47
+ return randomBytes(32).toString("base64");
48
+ }
49
+ function isEmptyAppKeyValue(value) {
50
+ const normalized = value.trim();
51
+ return normalized === "" || normalized === '""' || normalized === "''";
52
+ }
53
+ function renderEnvWithAppKey(existingContents, appKey) {
54
+ if (!existingContents) {
55
+ return {
56
+ contents: `APP_KEY=${appKey}
57
+ `,
58
+ changed: true
59
+ };
60
+ }
61
+ const lines = existingContents.replace(/\r\n/g, "\n").split("\n");
62
+ const appKeyIndex = lines.findIndex((line) => appKeyLinePattern.test(line));
63
+ if (appKeyIndex === -1) {
64
+ return {
65
+ contents: `${existingContents.replace(/\r\n/g, "\n").replace(/\n*$/, "")}
66
+ APP_KEY=${appKey}
67
+ `,
68
+ changed: true
69
+ };
70
+ }
71
+ const appKeyLine = lines[appKeyIndex];
72
+ const match = appKeyLine.match(appKeyLinePattern);
73
+ const prefix = match[1];
74
+ const value = match[2];
75
+ if (!isEmptyAppKeyValue(value)) {
76
+ return {
77
+ contents: existingContents,
78
+ changed: false
79
+ };
80
+ }
81
+ lines[appKeyIndex] = `${prefix}${appKey}`;
82
+ return {
83
+ contents: `${lines.join("\n").replace(/\n*$/, "")}
84
+ `,
85
+ changed: true
86
+ };
87
+ }
88
+ async function generateProjectAppKey(projectRoot) {
89
+ const envPath = resolve(projectRoot, ".env");
90
+ const existingContents = await readFile(envPath, "utf8").catch(() => void 0);
91
+ const key = generateAppKey();
92
+ const nextEnv = renderEnvWithAppKey(existingContents, key);
93
+ if (!nextEnv.changed || typeof nextEnv.contents !== "string") {
94
+ return {
95
+ envPath,
96
+ generated: false
97
+ };
98
+ }
99
+ await writeFile(envPath, nextEnv.contents, "utf8");
100
+ return {
101
+ envPath,
102
+ generated: true,
103
+ key
104
+ };
105
+ }
106
+
107
+ // src/cli.ts
40
108
  var runtimeModulePromise;
41
109
  var queueModulePromise;
42
110
  var cacheModulePromise;
43
111
  var queueMigrationsModulePromise;
44
112
  var cacheMigrationsModulePromise;
113
+ var mediaMigrationsModulePromise;
45
114
  var generatorsModulePromise;
46
115
  var broadcastModulePromise;
47
116
  var securityModulePromise;
@@ -51,55 +120,59 @@ var projectDiscoveryModulePromise;
51
120
  var projectRuntimeModulePromise;
52
121
  var projectScaffoldModulePromise;
53
122
  function loadRuntimeModule() {
54
- runtimeModulePromise ??= import("../runtime-4BV3JODY.mjs");
123
+ runtimeModulePromise ??= import("../runtime-MMQGO4PP.mjs");
55
124
  return runtimeModulePromise;
56
125
  }
57
126
  function loadQueueModule() {
58
- queueModulePromise ??= import("../queue-6OG7VJ34.mjs");
127
+ queueModulePromise ??= import("../queue-I66EISVS.mjs");
59
128
  return queueModulePromise;
60
129
  }
61
130
  function loadCacheModule() {
62
- cacheModulePromise ??= import("../cache-OWQY4E7W.mjs");
131
+ cacheModulePromise ??= import("../cache-NHCCHT44.mjs");
63
132
  return cacheModulePromise;
64
133
  }
65
134
  function loadQueueMigrationsModule() {
66
- queueMigrationsModulePromise ??= import("../queue-migrations-NK2EYX3J.mjs");
135
+ queueMigrationsModulePromise ??= import("../queue-migrations-UIAMAB6E.mjs");
67
136
  return queueMigrationsModulePromise;
68
137
  }
69
138
  function loadCacheMigrationsModule() {
70
- cacheMigrationsModulePromise ??= import("../cache-migrations-RVEA6CEU.mjs");
139
+ cacheMigrationsModulePromise ??= import("../cache-migrations-R2RL2RVD.mjs");
71
140
  return cacheMigrationsModulePromise;
72
141
  }
142
+ function loadMediaMigrationsModule() {
143
+ mediaMigrationsModulePromise ??= import("../media-migrations-UBAL2YVV.mjs");
144
+ return mediaMigrationsModulePromise;
145
+ }
73
146
  function loadGeneratorsModule() {
74
- generatorsModulePromise ??= import("../generators-WX45BI4U.mjs");
147
+ generatorsModulePromise ??= import("../generators-ZIWACCBE.mjs");
75
148
  return generatorsModulePromise;
76
149
  }
77
150
  function loadBroadcastModule() {
78
- broadcastModulePromise ??= import("../broadcast-YSIJCL3R.mjs");
151
+ broadcastModulePromise ??= import("../broadcast-RT5KVZWP.mjs");
79
152
  return broadcastModulePromise;
80
153
  }
81
154
  function loadSecurityModule() {
82
- securityModulePromise ??= import("../security-R7VH6W5H.mjs");
155
+ securityModulePromise ??= import("../security-OZXTMYXF.mjs");
83
156
  return securityModulePromise;
84
157
  }
85
158
  function loadDevModule() {
86
- devModulePromise ??= import("../dev-KQFT7RHR.mjs");
159
+ devModulePromise ??= import("../dev-OSLYSBTL.mjs");
87
160
  return devModulePromise;
88
161
  }
89
162
  function loadProjectConfigModule() {
90
- projectConfigModulePromise ??= import("../config-DMWBMMGD.mjs");
163
+ projectConfigModulePromise ??= import("../config-5JSC6KJG.mjs");
91
164
  return projectConfigModulePromise;
92
165
  }
93
166
  function loadProjectDiscoveryModule() {
94
- projectDiscoveryModulePromise ??= import("../discovery-R733D2PO.mjs");
167
+ projectDiscoveryModulePromise ??= import("../discovery-JLT2EOGH.mjs");
95
168
  return projectDiscoveryModulePromise;
96
169
  }
97
170
  function loadProjectRuntimeModule() {
98
- projectRuntimeModulePromise ??= import("../runtime-ANBO7VQM.mjs");
171
+ projectRuntimeModulePromise ??= import("../runtime-ZKD6URAV.mjs");
99
172
  return projectRuntimeModulePromise;
100
173
  }
101
174
  function loadProjectScaffoldModule() {
102
- projectScaffoldModulePromise ??= import("../scaffold-7OTDH4UR.mjs");
175
+ projectScaffoldModulePromise ??= import("../scaffold-ISDVICNQ.mjs");
103
176
  return projectScaffoldModulePromise;
104
177
  }
105
178
  async function resolveRuntimeExecutor(runtimeExecutor) {
@@ -134,6 +207,9 @@ var cacheExecutorLoaders = {
134
207
  runCacheClearCommand: async () => (await loadCacheModule()).runCacheClearCommand,
135
208
  runCacheForgetCommand: async () => (await loadCacheModule()).runCacheForgetCommand
136
209
  };
210
+ var mediaExecutorLoaders = {
211
+ runMediaTableCommand: async () => (await loadMediaMigrationsModule()).runMediaTableCommand
212
+ };
137
213
  var broadcastExecutorLoaders = {
138
214
  runBroadcastWorkCommand: async () => (await loadBroadcastModule()).runBroadcastWorkCommand
139
215
  };
@@ -151,6 +227,13 @@ async function resolveCacheExecutor(cacheExecutors, key) {
151
227
  }
152
228
  return cacheExecutorLoaders[key]();
153
229
  }
230
+ async function resolveMediaExecutor(mediaExecutors, key) {
231
+ const existing = mediaExecutors[key];
232
+ if (existing) {
233
+ return existing;
234
+ }
235
+ return mediaExecutorLoaders[key]();
236
+ }
154
237
  async function resolveBroadcastExecutor(broadcastExecutors, key) {
155
238
  const existing = broadcastExecutors[key];
156
239
  if (existing) {
@@ -215,7 +298,7 @@ function resolvePackageManagerDevCommand(packageManager) {
215
298
  return "yarn dev";
216
299
  }
217
300
  }
218
- function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, projectExecutors = {}, broadcastExecutors = {}, securityExecutors = {}, cacheExecutors = {}) {
301
+ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, projectExecutors = {}, broadcastExecutors = {}, securityExecutors = {}, cacheExecutors = {}, mediaExecutors = {}) {
219
302
  return [
220
303
  {
221
304
  name: "list",
@@ -256,7 +339,7 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
256
339
  const optionalPackages = normalizeOptionalPackages(
257
340
  (collectMultiStringFlag(commandContext.flags, "package") ?? []).flatMap((entry) => splitCsv(entry) ?? [])
258
341
  );
259
- const targetDir = resolve(commandContext.cwd, projectName);
342
+ const targetDir = resolve2(commandContext.cwd, projectName);
260
343
  const { scaffoldProject } = await loadProjectScaffoldModule();
261
344
  await scaffoldProject(targetDir, {
262
345
  projectName,
@@ -274,10 +357,26 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
274
357
  writeLine(context.stdout, ` ${resolvePackageManagerDevCommand(packageManager)}`);
275
358
  }
276
359
  },
360
+ {
361
+ name: "key:generate",
362
+ description: "Generate APP_KEY in the project .env file when it is missing.",
363
+ usage: "holo key:generate",
364
+ source: "internal",
365
+ async prepare() {
366
+ return { args: [], flags: {} };
367
+ },
368
+ async run() {
369
+ const result = await generateProjectAppKey(context.projectRoot);
370
+ writeLine(
371
+ context.stdout,
372
+ result.generated ? `Generated APP_KEY in ${result.envPath}.` : `APP_KEY is already set in ${result.envPath}.`
373
+ );
374
+ }
375
+ },
277
376
  {
278
377
  name: "install",
279
378
  description: "Install first-party Holo support into an existing project.",
280
- 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]",
379
+ 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]",
281
380
  source: "internal",
282
381
  async prepare(input) {
283
382
  const target = normalizeChoice(
@@ -307,6 +406,9 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
307
406
  if (target === "security" && requestedDriver) {
308
407
  throw new Error("The security installer does not support --driver.");
309
408
  }
409
+ if (target === "media" && requestedDriver) {
410
+ throw new Error("The media installer does not support --driver.");
411
+ }
310
412
  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;
311
413
  const socialProviders = target === "auth" ? (collectMultiStringFlag(input.flags, "provider") ?? []).flatMap((entry) => splitCsv(entry) ?? []).map((provider) => normalizeChoice(provider, SUPPORTED_AUTH_SOCIAL_PROVIDERS, "auth social provider")) : [];
312
414
  const social = target === "auth" ? resolveBooleanFlag(input.flags, "social") === true || socialProviders.length > 0 : false;
@@ -331,9 +433,9 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
331
433
  installEventsIntoProject,
332
434
  installQueueIntoProject: installQueueIntoProject2
333
435
  } = await loadProjectScaffoldModule();
436
+ 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"));
334
437
  const eventsResult = await installEventsIntoProject(context.projectRoot);
335
438
  let queueResult;
336
- 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"));
337
439
  if (!queueConfigured && isInteractive(context, commandContext.flags)) {
338
440
  const enableQueuedListeners = await confirm(
339
441
  context,
@@ -367,11 +469,13 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
367
469
  workos: commandContext.flags.workos === true,
368
470
  clerk: commandContext.flags.clerk === true
369
471
  });
370
- const changed2 = result2.updatedPackageJson || result2.createdAuthConfig || result2.createdSessionConfig || result2.createdUserModel || result2.createdMigrationFiles.length > 0 || result2.updatedEnv || result2.updatedEnvExample;
472
+ const changed2 = result2.updatedPackageJson || result2.createdAuthConfig || result2.createdSessionConfig || result2.createdSecurityConfig || result2.createdCorsConfig || result2.createdUserModel || result2.createdMigrationFiles.length > 0 || result2.updatedEnv || result2.updatedEnvExample;
371
473
  writeLine(context.stdout, changed2 ? "Installed auth support." : "Auth support is already installed.");
372
474
  if (result2.updatedPackageJson) writeLine(context.stdout, " - updated package.json");
373
475
  if (result2.createdAuthConfig) writeLine(context.stdout, " - created config/auth.ts");
374
476
  if (result2.createdSessionConfig) writeLine(context.stdout, " - created config/session.ts");
477
+ if (result2.createdSecurityConfig) writeLine(context.stdout, " - created config/security.ts");
478
+ if (result2.createdCorsConfig) writeLine(context.stdout, " - created config/cors.ts");
375
479
  if (result2.createdUserModel) writeLine(context.stdout, " - created server/models/User.ts");
376
480
  if (result2.updatedEnv) writeLine(context.stdout, " - updated .env");
377
481
  if (result2.updatedEnvExample) writeLine(context.stdout, " - updated .env.example");
@@ -405,9 +509,11 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
405
509
  if (target === "mail") {
406
510
  const { installMailIntoProject } = await loadProjectScaffoldModule();
407
511
  const result2 = await installMailIntoProject(context.projectRoot);
408
- const changed2 = result2.updatedPackageJson || result2.createdMailConfig || result2.createdMailDirectory;
512
+ const changed2 = result2.updatedPackageJson || result2.createdMailConfig || result2.createdMailDirectory || result2.updatedEnv || result2.updatedEnvExample;
409
513
  writeLine(context.stdout, changed2 ? "Installed mail support." : "Mail support is already installed.");
410
514
  if (result2.updatedPackageJson) writeLine(context.stdout, " - updated package.json");
515
+ if (result2.updatedEnv) writeLine(context.stdout, " - updated .env");
516
+ if (result2.updatedEnvExample) writeLine(context.stdout, " - updated .env.example");
411
517
  if (result2.createdMailConfig) writeLine(context.stdout, " - created config/mail.ts");
412
518
  if (result2.createdMailDirectory) writeLine(context.stdout, " - created server/mail");
413
519
  return;
@@ -430,10 +536,11 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
430
536
  if (target === "security") {
431
537
  const { installSecurityIntoProject } = await loadProjectScaffoldModule();
432
538
  const result2 = await installSecurityIntoProject(context.projectRoot);
433
- const changed2 = result2.updatedPackageJson || result2.createdSecurityConfig;
539
+ const changed2 = result2.updatedPackageJson || result2.createdSecurityConfig || result2.createdCorsConfig;
434
540
  writeLine(context.stdout, changed2 ? "Installed security support." : "Security support is already installed.");
435
541
  if (result2.updatedPackageJson) writeLine(context.stdout, " - updated package.json");
436
542
  if (result2.createdSecurityConfig) writeLine(context.stdout, " - created config/security.ts");
543
+ if (result2.createdCorsConfig) writeLine(context.stdout, " - created config/cors.ts");
437
544
  return;
438
545
  }
439
546
  if (target === "cache") {
@@ -451,6 +558,16 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
451
558
  if (result2.databaseDriver) writeLine(context.stdout, ' - run "holo cache:table" to create the cache tables');
452
559
  return;
453
560
  }
561
+ if (target === "media") {
562
+ const { installMediaIntoProject } = await loadProjectScaffoldModule();
563
+ const result2 = await installMediaIntoProject(context.projectRoot);
564
+ const changed2 = result2.createdMediaConfig || result2.updatedPackageJson || result2.createdMigrationFiles.length > 0;
565
+ writeLine(context.stdout, changed2 ? "Installed media support." : "Media support is already installed.");
566
+ if (result2.createdMediaConfig) writeLine(context.stdout, " - created config/media.ts");
567
+ if (result2.updatedPackageJson) writeLine(context.stdout, " - updated package.json");
568
+ if (result2.createdMigrationFiles.length > 0) writeLine(context.stdout, " - created media migration");
569
+ return;
570
+ }
454
571
  if (target !== "queue") {
455
572
  throw new Error(`Unsupported install target: ${target || "(empty)"}.`);
456
573
  }
@@ -467,6 +584,33 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
467
584
  if (result.createdJobsDirectory) writeLine(context.stdout, " - created server/jobs");
468
585
  }
469
586
  },
587
+ {
588
+ name: "auth:notifications:publish",
589
+ description: "Publish editable auth notification definitions into the application.",
590
+ usage: "holo auth:notifications:publish",
591
+ source: "internal",
592
+ async prepare() {
593
+ return { args: [], flags: {} };
594
+ },
595
+ async run() {
596
+ const { publishAuthNotificationsIntoProject } = await loadProjectScaffoldModule();
597
+ const result = await publishAuthNotificationsIntoProject(context.projectRoot);
598
+ const changed = result.createdFiles.length > 0;
599
+ writeLine(context.stdout, changed ? "Published auth notification files." : "Auth notification files are already published.");
600
+ for (const filePath of result.createdFiles) {
601
+ writeLine(context.stdout, ` - created ${filePath}`);
602
+ }
603
+ for (const filePath of result.skippedFiles) {
604
+ writeLine(context.stdout, ` - skipped existing ${filePath}`);
605
+ }
606
+ if (!result.hasMailDependency) {
607
+ writeLine(
608
+ context.stdout,
609
+ " - note: install @holo-js/mail or configure a notification mailer before email delivery can send."
610
+ );
611
+ }
612
+ }
613
+ },
470
614
  {
471
615
  name: "prepare",
472
616
  description: "Discover Holo resources and refresh generated registries.",
@@ -583,6 +727,19 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
583
727
  );
584
728
  }
585
729
  },
730
+ {
731
+ name: "media:table",
732
+ description: "Generate the media table migration.",
733
+ usage: "holo media:table",
734
+ source: "internal",
735
+ async prepare() {
736
+ return { args: [], flags: {} };
737
+ },
738
+ async run() {
739
+ const runMediaTableCommand = await resolveMediaExecutor(mediaExecutors, "runMediaTableCommand");
740
+ await runMediaTableCommand(context, context.projectRoot);
741
+ }
742
+ },
586
743
  {
587
744
  name: "queue:table",
588
745
  description: "Generate the database queue jobs table migration.",
@@ -1319,7 +1476,7 @@ async function runCli(argv, io) {
1319
1476
  };
1320
1477
  const internalCommands = createInternalCommands(internalContext);
1321
1478
  const registry = [...internalCommands];
1322
- 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";
1479
+ 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";
1323
1480
  if (!canSkipAppDiscovery) {
1324
1481
  const initialProject = await loadProject();
1325
1482
  const appCommands = (await (await loadProjectDiscoveryModule()).discoverAppCommands(projectRoot, initialProject.config)).map((entry) => createAppCommandDefinition(entry));
@@ -1369,4 +1526,4 @@ var exitCode = await runCli(process.argv.slice(2), {
1369
1526
  stdout: process.stdout,
1370
1527
  stderr: process.stderr
1371
1528
  });
1372
- process.exit(exitCode);
1529
+ process.exitCode = exitCode;
@@ -4,18 +4,18 @@ import {
4
4
  import "./chunk-D7O4SU6N.mjs";
5
5
  import {
6
6
  prepareProjectDiscovery
7
- } from "./chunk-S7P7EBM3.mjs";
8
- import "./chunk-MZXN2YMI.mjs";
7
+ } from "./chunk-UZTDQKIY.mjs";
8
+ import "./chunk-VCEO6N5T.mjs";
9
9
  import {
10
10
  loadProjectConfig
11
- } from "./chunk-GSQ3HTRO.mjs";
11
+ } from "./chunk-5BLEC66P.mjs";
12
12
  import {
13
13
  loadGeneratedProjectRegistry
14
- } from "./chunk-H7TJ4FB3.mjs";
14
+ } from "./chunk-MXKNQACM.mjs";
15
15
  import {
16
16
  importProjectModule,
17
17
  resolveProjectPackageImportSpecifier
18
- } from "./chunk-G5ADO27Q.mjs";
18
+ } from "./chunk-R6BWRY3E.mjs";
19
19
 
20
20
  // src/broadcast.ts
21
21
  import { basename, extname } from "path";
@@ -2,13 +2,13 @@ import {
2
2
  writeLine
3
3
  } from "./chunk-ZXDU7RHU.mjs";
4
4
  import "./chunk-D7O4SU6N.mjs";
5
- import "./chunk-S7P7EBM3.mjs";
6
- import "./chunk-MZXN2YMI.mjs";
7
- import "./chunk-GSQ3HTRO.mjs";
8
- import "./chunk-H7TJ4FB3.mjs";
5
+ import "./chunk-UZTDQKIY.mjs";
6
+ import "./chunk-VCEO6N5T.mjs";
7
+ import "./chunk-5BLEC66P.mjs";
8
+ import "./chunk-MXKNQACM.mjs";
9
9
  import {
10
10
  resolveProjectPackageImportSpecifier
11
- } from "./chunk-G5ADO27Q.mjs";
11
+ } from "./chunk-R6BWRY3E.mjs";
12
12
 
13
13
  // src/cache.ts
14
14
  import { loadConfigDirectory } from "@holo-js/config";
@@ -1,33 +1,33 @@
1
+ import {
2
+ runProjectPrepare
3
+ } from "./chunk-SCCPDJGO.mjs";
4
+ import "./chunk-F4MT6GBK.mjs";
1
5
  import {
2
6
  getRegistryMigrationSlug,
3
7
  hasRegisteredCreateTableMigration,
4
8
  hasRegisteredMigrationSlug,
5
9
  nextMigrationTemplate
6
- } from "./chunk-Q5F6C2D4.mjs";
10
+ } from "./chunk-BAFQ2GOA.mjs";
7
11
  import {
8
12
  writeLine
9
13
  } from "./chunk-ZXDU7RHU.mjs";
10
- import "./chunk-EUIVXVJL.mjs";
11
- import {
12
- runProjectPrepare
13
- } from "./chunk-ZLRO7HXY.mjs";
14
- import "./chunk-CUL4RJTG.mjs";
14
+ import "./chunk-57SJ566R.mjs";
15
15
  import "./chunk-D7O4SU6N.mjs";
16
16
  import {
17
17
  prepareProjectDiscovery
18
- } from "./chunk-S7P7EBM3.mjs";
19
- import "./chunk-MZXN2YMI.mjs";
18
+ } from "./chunk-UZTDQKIY.mjs";
19
+ import "./chunk-VCEO6N5T.mjs";
20
20
  import {
21
21
  ensureProjectConfig
22
- } from "./chunk-GSQ3HTRO.mjs";
22
+ } from "./chunk-5BLEC66P.mjs";
23
23
  import {
24
24
  loadGeneratedProjectRegistry
25
- } from "./chunk-H7TJ4FB3.mjs";
25
+ } from "./chunk-MXKNQACM.mjs";
26
26
  import {
27
27
  makeProjectRelativePath,
28
28
  resolveDefaultArtifactPath,
29
29
  writeTextFile
30
- } from "./chunk-G5ADO27Q.mjs";
30
+ } from "./chunk-R6BWRY3E.mjs";
31
31
 
32
32
  // src/cache-migrations.ts
33
33
  import { resolve } from "path";
@@ -106,17 +106,16 @@ async function runCacheTableCommand(io, projectRoot) {
106
106
  const migrationsDir = resolve(projectRoot, project.config.paths.migrations);
107
107
  const createdFiles = [];
108
108
  const resolvedTables = resolveDatabaseCacheTables(cacheConfig);
109
- const seenTables = /* @__PURE__ */ new Set();
110
- const seenLockTables = /* @__PURE__ */ new Set();
109
+ const seenPhysicalTables = /* @__PURE__ */ new Set();
111
110
  const seenSlugs = /* @__PURE__ */ new Map();
112
111
  for (const { table, lockTable } of resolvedTables) {
113
112
  const migrationName = normalizeCacheMigrationName(table);
114
113
  const previousTable = seenSlugs.get(migrationName);
115
- if (seenTables.has(table) || seenLockTables.has(lockTable) || previousTable && previousTable !== table) {
114
+ if (table === lockTable || seenPhysicalTables.has(table) || seenPhysicalTables.has(lockTable) || previousTable && previousTable !== table) {
116
115
  throw new Error(`A migration for cache tables "${table}" and "${lockTable}" already exists.`);
117
116
  }
118
- seenTables.add(table);
119
- seenLockTables.add(lockTable);
117
+ seenPhysicalTables.add(table);
118
+ seenPhysicalTables.add(lockTable);
120
119
  seenSlugs.set(migrationName, table);
121
120
  }
122
121
  for (const { table, lockTable } of resolvedTables) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readTextFile
3
- } from "./chunk-G5ADO27Q.mjs";
3
+ } from "./chunk-R6BWRY3E.mjs";
4
4
 
5
5
  // src/fs-utils.ts
6
6
  import { stat } from "fs/promises";