@holo-js/cli 0.1.9 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/holo.mjs +165 -81
- package/dist/broadcast-WI6PJS5P.mjs +203 -0
- package/dist/broadcast-YWS4N5QU.mjs +203 -0
- package/dist/{cache-ETOIQ5IG.mjs → cache-KWNQECAA.mjs} +6 -6
- package/dist/cache-QARFSW4F.mjs +66 -0
- package/dist/{cache-migrations-2GGI4TJK.mjs → cache-migrations-3OXR4FN5.mjs} +50 -30
- package/dist/cache-migrations-MDFMDVTK.mjs +173 -0
- package/dist/{chunk-IMOGEKB4.mjs → chunk-2DKQKZML.mjs} +188 -106
- package/dist/{chunk-7JR73TOH.mjs → chunk-2RGJTPYF.mjs} +36 -25
- package/dist/{chunk-ASTSSSL2.mjs → chunk-EWYXSN2C.mjs} +75 -122
- package/dist/{chunk-F4MT6GBK.mjs → chunk-FGQ2I2YH.mjs} +1 -1
- package/dist/chunk-I7QBCEV7.mjs +33 -0
- package/dist/{chunk-R6BWRY3E.mjs → chunk-ILU426CF.mjs} +3 -1
- package/dist/{chunk-HB4Q7VYK.mjs → chunk-IUDD5FYL.mjs} +28 -273
- package/dist/{chunk-WRZFATUT.mjs → chunk-KWRIBHC3.mjs} +229 -142
- package/dist/{chunk-57SJ566R.mjs → chunk-LBJAJLKU.mjs} +1 -1
- package/dist/{chunk-BAFQ2GOA.mjs → chunk-LXGQCG56.mjs} +1 -1
- package/dist/{chunk-SRPGIWCF.mjs → chunk-ONKESAQA.mjs} +2 -2
- package/dist/chunk-QA7TP5EO.mjs +448 -0
- package/dist/chunk-UPZH6KCF.mjs +3306 -0
- package/dist/{chunk-5EU32E7X.mjs → chunk-VRGB6DIS.mjs} +116 -12
- package/dist/{config-ARLE6PKR.mjs → config-TWEO2R4N.mjs} +3 -3
- package/dist/{dev-6RG5SSZ7.mjs → dev-2OULECTU.mjs} +7 -7
- package/dist/dev-PJMEGTAC.mjs +42 -0
- package/dist/{discovery-FCVGQQVD.mjs → discovery-7FXND7Y6.mjs} +3 -3
- package/dist/{generators-UI2LJK3O.mjs → generators-4BP7B47W.mjs} +11 -34
- package/dist/generators-Z4XLSMC7.mjs +520 -0
- package/dist/index.mjs +167 -83
- package/dist/{media-migrations-JQSDCC7S.mjs → media-migrations-BFEL7NFG.mjs} +9 -20
- package/dist/media-migrations-VR7DLLR6.mjs +106 -0
- package/dist/{queue-BY3PLH4I.mjs → queue-SVOJPTRO.mjs} +10 -10
- package/dist/queue-YCBQTCYI.mjs +625 -0
- package/dist/{queue-migrations-YZUKEZK7.mjs → queue-migrations-HPXOO3NA.mjs} +13 -12
- package/dist/queue-migrations-X4P7FZKJ.mjs +167 -0
- package/dist/{runtime-BI343WHS.mjs → runtime-CPKR663Y.mjs} +9 -9
- package/dist/runtime-GIE56H47.mjs +57 -0
- package/dist/{runtime-ZKD6URAV.mjs → runtime-GSXF4NB3.mjs} +1 -1
- package/dist/runtime-worker.d.ts +2 -0
- package/dist/runtime-worker.mjs +242 -0
- package/dist/{scaffold-UBOS2NZR.mjs → scaffold-3QPGYQEQ.mjs} +9 -5
- package/dist/scaffold-RGAAHC6I.mjs +139 -0
- package/dist/{security-TYPVOYGF.mjs → security-7H5TNHZY.mjs} +6 -6
- package/dist/security-BZGD6ONY.mjs +71 -0
- package/package.json +9 -7
- package/dist/broadcast-VR46UZEL.mjs +0 -84
- package/dist/chunk-ZXDU7RHU.mjs +0 -9
package/dist/bin/holo.mjs
CHANGED
|
@@ -4,35 +4,38 @@ import {
|
|
|
4
4
|
SUPPORTED_INSTALL_TARGETS,
|
|
5
5
|
SUPPORTED_QUEUE_INSTALL_DRIVERS,
|
|
6
6
|
collectMultiStringFlag,
|
|
7
|
-
confirm,
|
|
8
7
|
ensureRequiredArg,
|
|
9
8
|
isInteractive,
|
|
10
9
|
normalizeChoice,
|
|
11
10
|
normalizeOptionalPackages,
|
|
12
11
|
parseNumberFlag,
|
|
13
12
|
parseTokens,
|
|
13
|
+
promptChoice,
|
|
14
|
+
promptMultiChoice,
|
|
14
15
|
resolveBooleanFlag,
|
|
15
16
|
resolveNewProjectInput,
|
|
16
17
|
resolveStringFlag,
|
|
17
18
|
splitCsv
|
|
18
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-VRGB6DIS.mjs";
|
|
19
20
|
import {
|
|
20
21
|
hasProjectDependency
|
|
21
|
-
} from "../chunk-
|
|
22
|
+
} from "../chunk-FGQ2I2YH.mjs";
|
|
22
23
|
import {
|
|
24
|
+
runWithSpinner,
|
|
25
|
+
supportsSpinner,
|
|
23
26
|
writeLine
|
|
24
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-I7QBCEV7.mjs";
|
|
25
28
|
import {
|
|
26
29
|
fileExists
|
|
27
|
-
} from "../chunk-
|
|
30
|
+
} from "../chunk-LBJAJLKU.mjs";
|
|
28
31
|
import "../chunk-D7O4SU6N.mjs";
|
|
29
|
-
import "../chunk-
|
|
30
|
-
import "../chunk-
|
|
31
|
-
import "../chunk-
|
|
32
|
-
import "../chunk-
|
|
32
|
+
import "../chunk-2RGJTPYF.mjs";
|
|
33
|
+
import "../chunk-UPZH6KCF.mjs";
|
|
34
|
+
import "../chunk-ONKESAQA.mjs";
|
|
35
|
+
import "../chunk-2DKQKZML.mjs";
|
|
33
36
|
import {
|
|
34
37
|
SUPPORTED_AUTH_SOCIAL_PROVIDERS
|
|
35
|
-
} from "../chunk-
|
|
38
|
+
} from "../chunk-ILU426CF.mjs";
|
|
36
39
|
|
|
37
40
|
// src/cli.ts
|
|
38
41
|
import { resolve as resolve2 } from "path";
|
|
@@ -105,6 +108,10 @@ async function generateProjectAppKey(projectRoot) {
|
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
// src/cli.ts
|
|
111
|
+
var AUTH_INSTALL_FEATURES = ["social", "workos", "clerk"];
|
|
112
|
+
var AUTH_SOCIAL_PROVIDER_MODES = ["default", "specific"];
|
|
113
|
+
var EVENTS_QUEUE_ACTIONS = ["skip", "enable"];
|
|
114
|
+
var MODEL_GENERATOR_OPTIONS = ["migration", "observer", "seeder", "factory"];
|
|
108
115
|
var runtimeModulePromise;
|
|
109
116
|
var queueModulePromise;
|
|
110
117
|
var cacheModulePromise;
|
|
@@ -120,59 +127,59 @@ var projectDiscoveryModulePromise;
|
|
|
120
127
|
var projectRuntimeModulePromise;
|
|
121
128
|
var projectScaffoldModulePromise;
|
|
122
129
|
function loadRuntimeModule() {
|
|
123
|
-
runtimeModulePromise ??= import("../runtime-
|
|
130
|
+
runtimeModulePromise ??= import("../runtime-CPKR663Y.mjs");
|
|
124
131
|
return runtimeModulePromise;
|
|
125
132
|
}
|
|
126
133
|
function loadQueueModule() {
|
|
127
|
-
queueModulePromise ??= import("../queue-
|
|
134
|
+
queueModulePromise ??= import("../queue-SVOJPTRO.mjs");
|
|
128
135
|
return queueModulePromise;
|
|
129
136
|
}
|
|
130
137
|
function loadCacheModule() {
|
|
131
|
-
cacheModulePromise ??= import("../cache-
|
|
138
|
+
cacheModulePromise ??= import("../cache-QARFSW4F.mjs");
|
|
132
139
|
return cacheModulePromise;
|
|
133
140
|
}
|
|
134
141
|
function loadQueueMigrationsModule() {
|
|
135
|
-
queueMigrationsModulePromise ??= import("../queue-migrations-
|
|
142
|
+
queueMigrationsModulePromise ??= import("../queue-migrations-HPXOO3NA.mjs");
|
|
136
143
|
return queueMigrationsModulePromise;
|
|
137
144
|
}
|
|
138
145
|
function loadCacheMigrationsModule() {
|
|
139
|
-
cacheMigrationsModulePromise ??= import("../cache-migrations-
|
|
146
|
+
cacheMigrationsModulePromise ??= import("../cache-migrations-3OXR4FN5.mjs");
|
|
140
147
|
return cacheMigrationsModulePromise;
|
|
141
148
|
}
|
|
142
149
|
function loadMediaMigrationsModule() {
|
|
143
|
-
mediaMigrationsModulePromise ??= import("../media-migrations-
|
|
150
|
+
mediaMigrationsModulePromise ??= import("../media-migrations-VR7DLLR6.mjs");
|
|
144
151
|
return mediaMigrationsModulePromise;
|
|
145
152
|
}
|
|
146
153
|
function loadGeneratorsModule() {
|
|
147
|
-
generatorsModulePromise ??= import("../generators-
|
|
154
|
+
generatorsModulePromise ??= import("../generators-4BP7B47W.mjs");
|
|
148
155
|
return generatorsModulePromise;
|
|
149
156
|
}
|
|
150
157
|
function loadBroadcastModule() {
|
|
151
|
-
broadcastModulePromise ??= import("../broadcast-
|
|
158
|
+
broadcastModulePromise ??= import("../broadcast-YWS4N5QU.mjs");
|
|
152
159
|
return broadcastModulePromise;
|
|
153
160
|
}
|
|
154
161
|
function loadSecurityModule() {
|
|
155
|
-
securityModulePromise ??= import("../security-
|
|
162
|
+
securityModulePromise ??= import("../security-7H5TNHZY.mjs");
|
|
156
163
|
return securityModulePromise;
|
|
157
164
|
}
|
|
158
165
|
function loadDevModule() {
|
|
159
|
-
devModulePromise ??= import("../dev-
|
|
166
|
+
devModulePromise ??= import("../dev-2OULECTU.mjs");
|
|
160
167
|
return devModulePromise;
|
|
161
168
|
}
|
|
162
169
|
function loadProjectConfigModule() {
|
|
163
|
-
projectConfigModulePromise ??= import("../config-
|
|
170
|
+
projectConfigModulePromise ??= import("../config-TWEO2R4N.mjs");
|
|
164
171
|
return projectConfigModulePromise;
|
|
165
172
|
}
|
|
166
173
|
function loadProjectDiscoveryModule() {
|
|
167
|
-
projectDiscoveryModulePromise ??= import("../discovery-
|
|
174
|
+
projectDiscoveryModulePromise ??= import("../discovery-7FXND7Y6.mjs");
|
|
168
175
|
return projectDiscoveryModulePromise;
|
|
169
176
|
}
|
|
170
177
|
function loadProjectRuntimeModule() {
|
|
171
|
-
projectRuntimeModulePromise ??= import("../runtime-
|
|
178
|
+
projectRuntimeModulePromise ??= import("../runtime-GSXF4NB3.mjs");
|
|
172
179
|
return projectRuntimeModulePromise;
|
|
173
180
|
}
|
|
174
181
|
function loadProjectScaffoldModule() {
|
|
175
|
-
projectScaffoldModulePromise ??= import("../scaffold-
|
|
182
|
+
projectScaffoldModulePromise ??= import("../scaffold-3QPGYQEQ.mjs");
|
|
176
183
|
return projectScaffoldModulePromise;
|
|
177
184
|
}
|
|
178
185
|
async function resolveRuntimeExecutor(runtimeExecutor) {
|
|
@@ -184,12 +191,15 @@ var projectExecutorLoaders = {
|
|
|
184
191
|
runProjectLifecycleScript: async () => (await loadDevModule()).runProjectLifecycleScript,
|
|
185
192
|
runProjectDependencyInstall: async () => (await loadDevModule()).runProjectDependencyInstall
|
|
186
193
|
};
|
|
187
|
-
async function
|
|
188
|
-
const existing =
|
|
194
|
+
async function resolveExecutor(executors, loaders, key) {
|
|
195
|
+
const existing = executors[key];
|
|
189
196
|
if (existing) {
|
|
190
197
|
return existing;
|
|
191
198
|
}
|
|
192
|
-
return
|
|
199
|
+
return await loaders[key]();
|
|
200
|
+
}
|
|
201
|
+
async function resolveProjectExecutor(projectExecutors, key) {
|
|
202
|
+
return await resolveExecutor(projectExecutors, projectExecutorLoaders, key);
|
|
193
203
|
}
|
|
194
204
|
var queueExecutorLoaders = {
|
|
195
205
|
runQueueFailedTableCommand: async () => (await loadQueueMigrationsModule()).runQueueFailedTableCommand,
|
|
@@ -215,32 +225,16 @@ var broadcastExecutorLoaders = {
|
|
|
215
225
|
runBroadcastWorkCommand: async () => (await loadBroadcastModule()).runBroadcastWorkCommand
|
|
216
226
|
};
|
|
217
227
|
async function resolveQueueExecutor(queueExecutors, key) {
|
|
218
|
-
|
|
219
|
-
if (existing) {
|
|
220
|
-
return existing;
|
|
221
|
-
}
|
|
222
|
-
return queueExecutorLoaders[key]();
|
|
228
|
+
return await resolveExecutor(queueExecutors, queueExecutorLoaders, key);
|
|
223
229
|
}
|
|
224
230
|
async function resolveCacheExecutor(cacheExecutors, key) {
|
|
225
|
-
|
|
226
|
-
if (existing) {
|
|
227
|
-
return existing;
|
|
228
|
-
}
|
|
229
|
-
return cacheExecutorLoaders[key]();
|
|
231
|
+
return await resolveExecutor(cacheExecutors, cacheExecutorLoaders, key);
|
|
230
232
|
}
|
|
231
233
|
async function resolveMediaExecutor(mediaExecutors, key) {
|
|
232
|
-
|
|
233
|
-
if (existing) {
|
|
234
|
-
return existing;
|
|
235
|
-
}
|
|
236
|
-
return mediaExecutorLoaders[key]();
|
|
234
|
+
return await resolveExecutor(mediaExecutors, mediaExecutorLoaders, key);
|
|
237
235
|
}
|
|
238
236
|
async function resolveBroadcastExecutor(broadcastExecutors, key) {
|
|
239
|
-
|
|
240
|
-
if (existing) {
|
|
241
|
-
return existing;
|
|
242
|
-
}
|
|
243
|
-
return broadcastExecutorLoaders[key]();
|
|
237
|
+
return await resolveExecutor(broadcastExecutors, broadcastExecutorLoaders, key);
|
|
244
238
|
}
|
|
245
239
|
async function resolveGeneratorCommand(key) {
|
|
246
240
|
return (await loadGeneratorsModule())[key];
|
|
@@ -295,10 +289,12 @@ async function runProjectDependencyInstallAfterPackageJsonUpdate(context, projec
|
|
|
295
289
|
const runProjectPrepare = await resolveProjectExecutor(projectExecutors, "runProjectPrepare");
|
|
296
290
|
await runProjectPrepare(context.projectRoot, context);
|
|
297
291
|
}
|
|
298
|
-
async function runProjectDependencyInstallForProject(context, projectExecutors, projectRoot) {
|
|
292
|
+
async function runProjectDependencyInstallForProject(context, projectExecutors, projectRoot, options = {}) {
|
|
299
293
|
const runProjectDependencyInstall = await resolveProjectExecutor(projectExecutors, "runProjectDependencyInstall");
|
|
300
294
|
await runProjectDependencyInstall(context, projectRoot);
|
|
301
|
-
|
|
295
|
+
if (options.writeStatus ?? true) {
|
|
296
|
+
writeLine(context.stdout, " - installed dependencies");
|
|
297
|
+
}
|
|
302
298
|
}
|
|
303
299
|
function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, projectExecutors = {}, broadcastExecutors = {}, securityExecutors = {}, cacheExecutors = {}, mediaExecutors = {}) {
|
|
304
300
|
return [
|
|
@@ -317,7 +313,7 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
|
|
|
317
313
|
{
|
|
318
314
|
name: "new",
|
|
319
315
|
description: "Scaffold a new Holo project",
|
|
320
|
-
usage: "holo new <name> [--framework <nuxt|next|sveltekit>] [--database <sqlite|mysql|postgres>] [--package-manager <bun|npm|pnpm|yarn>] [--package <storage|events|queue|validation|forms|auth|authorization|notifications|mail|broadcast|security|cache>] [--storage-default-disk <local|public>]",
|
|
316
|
+
usage: "holo new <name> [--framework <nuxt|next|sveltekit>] [--database <sqlite|mysql|postgres>] [--package-manager <bun|npm|pnpm|yarn>] [--package <storage|events|queue|validation|forms|auth|authorization|notifications|mail|broadcast|realtime|security|cache>] [--storage-default-disk <local|public>]",
|
|
321
317
|
source: "internal",
|
|
322
318
|
async prepare(input) {
|
|
323
319
|
const resolved = await resolveNewProjectInput(context, input);
|
|
@@ -343,16 +339,28 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
|
|
|
343
339
|
);
|
|
344
340
|
const targetDir = resolve2(commandContext.cwd, projectName);
|
|
345
341
|
const { scaffoldProject } = await loadProjectScaffoldModule();
|
|
346
|
-
await
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
342
|
+
await runWithSpinner(
|
|
343
|
+
context,
|
|
344
|
+
"Creating project files...",
|
|
345
|
+
() => scaffoldProject(targetDir, {
|
|
346
|
+
projectName,
|
|
347
|
+
framework,
|
|
348
|
+
databaseDriver,
|
|
349
|
+
packageManager,
|
|
350
|
+
storageDefaultDisk,
|
|
351
|
+
optionalPackages
|
|
352
|
+
}),
|
|
353
|
+
"Project files created."
|
|
354
|
+
);
|
|
354
355
|
writeLine(context.stdout, `Created Holo project: ${targetDir}`);
|
|
355
|
-
await
|
|
356
|
+
await runWithSpinner(
|
|
357
|
+
context,
|
|
358
|
+
"Installing dependencies...",
|
|
359
|
+
() => runProjectDependencyInstallForProject(context, projectExecutors, targetDir, {
|
|
360
|
+
writeStatus: !supportsSpinner(context)
|
|
361
|
+
}),
|
|
362
|
+
"Dependencies installed."
|
|
363
|
+
);
|
|
356
364
|
writeLine(context.stdout);
|
|
357
365
|
writeLine(context.stdout, "Next steps");
|
|
358
366
|
writeLine(context.stdout, ` cd ${projectName}`);
|
|
@@ -378,11 +386,13 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
|
|
|
378
386
|
{
|
|
379
387
|
name: "install",
|
|
380
388
|
description: "Install first-party Holo support into an existing project.",
|
|
381
|
-
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]",
|
|
389
|
+
usage: "holo install <queue|events|auth|authorization|notifications|mail|broadcast|realtime|security|cache|media> [--driver <queue: sync|file|redis|database; cache: file|redis|database>] [--social] [--provider <google|github|discord|facebook|apple|linkedin>] [--workos] [--clerk]",
|
|
382
390
|
source: "internal",
|
|
383
391
|
async prepare(input) {
|
|
392
|
+
const interactive = isInteractive(context, input.flags);
|
|
393
|
+
const requestedTarget = input.args[0]?.trim();
|
|
384
394
|
const target = normalizeChoice(
|
|
385
|
-
await ensureRequiredArg(context, input, 0, "Install target"),
|
|
395
|
+
requestedTarget || (interactive ? await promptChoice(context, "Install target", SUPPORTED_INSTALL_TARGETS, "queue") : await ensureRequiredArg(context, input, 0, "Install target")),
|
|
386
396
|
SUPPORTED_INSTALL_TARGETS,
|
|
387
397
|
"install target"
|
|
388
398
|
);
|
|
@@ -405,17 +415,65 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
|
|
|
405
415
|
if (target === "broadcast" && requestedDriver) {
|
|
406
416
|
throw new Error("The broadcast installer does not support --driver.");
|
|
407
417
|
}
|
|
418
|
+
if (target === "realtime" && requestedDriver) {
|
|
419
|
+
throw new Error("The realtime installer does not support --driver.");
|
|
420
|
+
}
|
|
408
421
|
if (target === "security" && requestedDriver) {
|
|
409
422
|
throw new Error("The security installer does not support --driver.");
|
|
410
423
|
}
|
|
411
424
|
if (target === "media" && requestedDriver) {
|
|
412
425
|
throw new Error("The media installer does not support --driver.");
|
|
413
426
|
}
|
|
414
|
-
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;
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
427
|
+
const driver = target === "queue" ? requestedDriver ? normalizeChoice(requestedDriver, SUPPORTED_QUEUE_INSTALL_DRIVERS, "queue driver") : interactive ? await promptChoice(context, "Queue driver", SUPPORTED_QUEUE_INSTALL_DRIVERS, "sync") : "sync" : target === "cache" ? requestedDriver ? normalizeChoice(requestedDriver, SUPPORTED_CACHE_INSTALL_DRIVERS, "cache driver") : interactive ? await promptChoice(context, "Cache driver", SUPPORTED_CACHE_INSTALL_DRIVERS, "file") : "file" : void 0;
|
|
428
|
+
let socialProviders = target === "auth" ? (collectMultiStringFlag(input.flags, "provider") ?? []).flatMap((entry) => splitCsv(entry) ?? []).map((provider) => normalizeChoice(provider, SUPPORTED_AUTH_SOCIAL_PROVIDERS, "auth social provider")) : [];
|
|
429
|
+
let social = target === "auth" ? resolveBooleanFlag(input.flags, "social") === true || socialProviders.length > 0 : false;
|
|
430
|
+
let workos = target === "auth" ? resolveBooleanFlag(input.flags, "workos") === true : false;
|
|
431
|
+
let clerk = target === "auth" ? resolveBooleanFlag(input.flags, "clerk") === true : false;
|
|
432
|
+
const authFlagsProvided = "social" in input.flags || "provider" in input.flags || "workos" in input.flags || "clerk" in input.flags;
|
|
433
|
+
if (target === "auth" && interactive && !authFlagsProvided) {
|
|
434
|
+
const features = await promptMultiChoice(
|
|
435
|
+
context,
|
|
436
|
+
"Auth providers",
|
|
437
|
+
AUTH_INSTALL_FEATURES,
|
|
438
|
+
{
|
|
439
|
+
labels: {
|
|
440
|
+
social: "Social OAuth",
|
|
441
|
+
workos: "WorkOS",
|
|
442
|
+
clerk: "Clerk"
|
|
443
|
+
},
|
|
444
|
+
hints: {
|
|
445
|
+
social: "Google, GitHub, Discord, Facebook, Apple, or LinkedIn"
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
);
|
|
449
|
+
social = features.includes("social");
|
|
450
|
+
workos = features.includes("workos");
|
|
451
|
+
clerk = features.includes("clerk");
|
|
452
|
+
if (social) {
|
|
453
|
+
const providerMode = await promptChoice(
|
|
454
|
+
context,
|
|
455
|
+
"Social provider setup",
|
|
456
|
+
AUTH_SOCIAL_PROVIDER_MODES,
|
|
457
|
+
"default",
|
|
458
|
+
{
|
|
459
|
+
labels: {
|
|
460
|
+
default: "Default social setup",
|
|
461
|
+
specific: "Choose specific providers"
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
);
|
|
465
|
+
if (providerMode === "specific") {
|
|
466
|
+
socialProviders = [
|
|
467
|
+
...await promptMultiChoice(
|
|
468
|
+
context,
|
|
469
|
+
"Social providers",
|
|
470
|
+
SUPPORTED_AUTH_SOCIAL_PROVIDERS,
|
|
471
|
+
{ required: true }
|
|
472
|
+
)
|
|
473
|
+
];
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
419
477
|
return {
|
|
420
478
|
args: [target],
|
|
421
479
|
flags: {
|
|
@@ -439,12 +497,19 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
|
|
|
439
497
|
const eventsResult = await installEventsIntoProject(context.projectRoot);
|
|
440
498
|
let queueResult;
|
|
441
499
|
if (!queueConfigured && isInteractive(context, commandContext.flags)) {
|
|
442
|
-
const
|
|
500
|
+
const queueAction = await promptChoice(
|
|
443
501
|
context,
|
|
444
|
-
"
|
|
445
|
-
|
|
502
|
+
"Queued listeners",
|
|
503
|
+
EVENTS_QUEUE_ACTIONS,
|
|
504
|
+
"skip",
|
|
505
|
+
{
|
|
506
|
+
labels: {
|
|
507
|
+
skip: "Skip queued listeners",
|
|
508
|
+
enable: "Enable queued listeners"
|
|
509
|
+
}
|
|
510
|
+
}
|
|
446
511
|
);
|
|
447
|
-
if (
|
|
512
|
+
if (queueAction === "enable") {
|
|
448
513
|
queueResult = await installQueueIntoProject2(context.projectRoot, { driver: "sync" });
|
|
449
514
|
}
|
|
450
515
|
}
|
|
@@ -545,6 +610,17 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
|
|
|
545
610
|
await runProjectDependencyInstallAfterPackageJsonUpdate(context, projectExecutors, result2.updatedPackageJson);
|
|
546
611
|
return;
|
|
547
612
|
}
|
|
613
|
+
if (target === "realtime") {
|
|
614
|
+
const { installRealtimeIntoProject } = await loadProjectScaffoldModule();
|
|
615
|
+
const result2 = await installRealtimeIntoProject(context.projectRoot);
|
|
616
|
+
const changed2 = result2.updatedPackageJson || result2.createdRealtimeDirectory || result2.createdFrameworkSetup;
|
|
617
|
+
writeLine(context.stdout, changed2 ? "Installed realtime support." : "Realtime support is already installed.");
|
|
618
|
+
if (result2.updatedPackageJson) writeLine(context.stdout, " - updated package.json");
|
|
619
|
+
if (result2.createdRealtimeDirectory) writeLine(context.stdout, " - created server/realtime");
|
|
620
|
+
if (result2.createdFrameworkSetup) writeLine(context.stdout, " - created realtime framework setup");
|
|
621
|
+
await runProjectDependencyInstallAfterPackageJsonUpdate(context, projectExecutors, result2.updatedPackageJson);
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
548
624
|
if (target === "security") {
|
|
549
625
|
const { installSecurityIntoProject } = await loadProjectScaffoldModule();
|
|
550
626
|
const result2 = await installSecurityIntoProject(context.projectRoot);
|
|
@@ -1041,17 +1117,25 @@ function createInternalCommands(context, runtimeExecutor, queueExecutors = {}, p
|
|
|
1041
1117
|
};
|
|
1042
1118
|
if (isInteractive(context, input.flags)) {
|
|
1043
1119
|
const noneSelected = [flags.migration, flags.observer, flags.seeder, flags.factory].every((value) => value !== true);
|
|
1044
|
-
if (noneSelected
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1120
|
+
if (noneSelected) {
|
|
1121
|
+
const selectedArtifacts = await promptMultiChoice(
|
|
1122
|
+
context,
|
|
1123
|
+
"Model artifacts",
|
|
1124
|
+
MODEL_GENERATOR_OPTIONS,
|
|
1125
|
+
{
|
|
1126
|
+
initialValues: ["migration"],
|
|
1127
|
+
labels: {
|
|
1128
|
+
migration: "Migration",
|
|
1129
|
+
observer: "Observer",
|
|
1130
|
+
seeder: "Seeder",
|
|
1131
|
+
factory: "Factory"
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
);
|
|
1135
|
+
flags.migration = selectedArtifacts.includes("migration");
|
|
1136
|
+
flags.observer = selectedArtifacts.includes("observer");
|
|
1137
|
+
flags.seeder = selectedArtifacts.includes("seeder");
|
|
1138
|
+
flags.factory = selectedArtifacts.includes("factory");
|
|
1055
1139
|
}
|
|
1056
1140
|
}
|
|
1057
1141
|
return {
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import {
|
|
2
|
+
initializeProjectRuntime
|
|
3
|
+
} from "./chunk-IUDD5FYL.mjs";
|
|
4
|
+
import "./chunk-LBJAJLKU.mjs";
|
|
5
|
+
import {
|
|
6
|
+
writeLine
|
|
7
|
+
} from "./chunk-I7QBCEV7.mjs";
|
|
8
|
+
import "./chunk-D7O4SU6N.mjs";
|
|
9
|
+
import {
|
|
10
|
+
prepareProjectDiscovery
|
|
11
|
+
} from "./chunk-2RGJTPYF.mjs";
|
|
12
|
+
import "./chunk-KWRIBHC3.mjs";
|
|
13
|
+
import {
|
|
14
|
+
loadProjectConfig
|
|
15
|
+
} from "./chunk-ONKESAQA.mjs";
|
|
16
|
+
import {
|
|
17
|
+
loadGeneratedProjectRegistry
|
|
18
|
+
} from "./chunk-2DKQKZML.mjs";
|
|
19
|
+
import {
|
|
20
|
+
importProjectModule,
|
|
21
|
+
resolveProjectPackageImportSpecifier
|
|
22
|
+
} from "./chunk-ILU426CF.mjs";
|
|
23
|
+
|
|
24
|
+
// src/broadcast.ts
|
|
25
|
+
import { basename, extname, resolve } from "path";
|
|
26
|
+
import { readFile } from "fs/promises";
|
|
27
|
+
import { loadConfigDirectory } from "@holo-js/config";
|
|
28
|
+
function hasLoadedRedisConfigSection(loadedFiles) {
|
|
29
|
+
return Array.isArray(loadedFiles) && loadedFiles.some((filePath) => {
|
|
30
|
+
return basename(filePath, extname(filePath)) === "redis";
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
async function hasProjectPackageDependency(projectRoot, packageName) {
|
|
34
|
+
try {
|
|
35
|
+
const manifest = JSON.parse(await readFile(resolve(projectRoot, "package.json"), "utf8"));
|
|
36
|
+
return Boolean(
|
|
37
|
+
manifest.dependencies?.[packageName] || manifest.devDependencies?.[packageName] || manifest.optionalDependencies?.[packageName] || manifest.peerDependencies?.[packageName]
|
|
38
|
+
);
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function loadBroadcastCliModule(projectRoot) {
|
|
44
|
+
try {
|
|
45
|
+
return await import(resolveProjectPackageImportSpecifier(projectRoot, "@holo-js/broadcast"));
|
|
46
|
+
} catch (error) {
|
|
47
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Unable to load @holo-js/broadcast from ${projectRoot}. Install it with "holo install broadcast". ${details}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function loadRealtimeServerModule(projectRoot) {
|
|
54
|
+
const specifier = resolveProjectPackageImportSpecifier(projectRoot, "@holo-js/realtime/server");
|
|
55
|
+
try {
|
|
56
|
+
return await import(specifier);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
59
|
+
if (/Cannot find module|ERR_MODULE_NOT_FOUND|MODULE_NOT_FOUND/i.test(details) && details.includes(specifier)) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function safeDecodeCookieSegment(value) {
|
|
66
|
+
try {
|
|
67
|
+
return decodeURIComponent(value);
|
|
68
|
+
} catch {
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function readCookieFromHeader(header, name) {
|
|
73
|
+
for (const segment of header?.split(";") ?? []) {
|
|
74
|
+
const separator = segment.indexOf("=");
|
|
75
|
+
if (separator <= 0) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const cookieName = safeDecodeCookieSegment(segment.slice(0, separator).trim());
|
|
79
|
+
if (cookieName === name) {
|
|
80
|
+
return safeDecodeCookieSegment(segment.slice(separator + 1).trim());
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
function createRealtimeAuthRequestAccessors(headers) {
|
|
86
|
+
return {
|
|
87
|
+
async getCookie(name) {
|
|
88
|
+
return readCookieFromHeader(headers.get("cookie"), name);
|
|
89
|
+
},
|
|
90
|
+
async getHeader(name) {
|
|
91
|
+
return headers.get(name) ?? void 0;
|
|
92
|
+
},
|
|
93
|
+
async appendResponseCookie(_cookie) {
|
|
94
|
+
},
|
|
95
|
+
async redirectResponse(url) {
|
|
96
|
+
throw new Error(`Realtime auth attempted to redirect to "${url}". Realtime requests cannot redirect.`);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async function createRealtimeWorkerBindings(projectRoot) {
|
|
101
|
+
if (!await hasProjectPackageDependency(projectRoot, "@holo-js/realtime")) {
|
|
102
|
+
return void 0;
|
|
103
|
+
}
|
|
104
|
+
const realtime = await loadRealtimeServerModule(projectRoot);
|
|
105
|
+
if (!realtime) {
|
|
106
|
+
return void 0;
|
|
107
|
+
}
|
|
108
|
+
const runtime = await initializeProjectRuntime(projectRoot);
|
|
109
|
+
realtime.configureRealtimeRuntime?.({
|
|
110
|
+
async runWithAuthRequestAccessors(accessors, callback) {
|
|
111
|
+
const runner = runtime.runWithAuthRequestAccessors;
|
|
112
|
+
return runner ? await runner(accessors, callback) : await callback();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
116
|
+
const resolveDefinition = async (name) => {
|
|
117
|
+
const cached = definitions.get(name);
|
|
118
|
+
if (cached) {
|
|
119
|
+
return await cached;
|
|
120
|
+
}
|
|
121
|
+
const resolved = realtime.resolveRealtimeDefinition(name, {
|
|
122
|
+
projectRoot,
|
|
123
|
+
importModule: async (absolutePath) => await importProjectModule(projectRoot, absolutePath)
|
|
124
|
+
}).catch((error) => {
|
|
125
|
+
definitions.delete(name);
|
|
126
|
+
throw error;
|
|
127
|
+
});
|
|
128
|
+
definitions.set(name, resolved);
|
|
129
|
+
return await resolved;
|
|
130
|
+
};
|
|
131
|
+
const withRealtimeRequest = async (context, callback) => {
|
|
132
|
+
return await callback(createRealtimeAuthRequestAccessors(context.headers));
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
async query(name, args, context) {
|
|
136
|
+
return await withRealtimeRequest(context, async (authRequest) => {
|
|
137
|
+
return await realtime.executeRealtimeQuery(await resolveDefinition(name), args, { authRequest });
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
async mutate(name, args, context) {
|
|
141
|
+
return await withRealtimeRequest(context, async (authRequest) => {
|
|
142
|
+
return await realtime.executeRealtimeMutation(await resolveDefinition(name), args, { authRequest });
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
async subscribe(name, args, options) {
|
|
146
|
+
return await withRealtimeRequest(options.context, async (authRequest) => {
|
|
147
|
+
return await realtime.subscribeRealtimeQuery(await resolveDefinition(name), args, {
|
|
148
|
+
onData: options.onData,
|
|
149
|
+
onError: options.onError
|
|
150
|
+
}, { authRequest });
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
async function runBroadcastWorkCommand(io, projectRoot, dependencies = {}) {
|
|
156
|
+
const loadConfig = dependencies.loadConfig ?? loadConfigDirectory;
|
|
157
|
+
const loadModule = dependencies.loadModule ?? loadBroadcastCliModule;
|
|
158
|
+
const config = await loadConfig(projectRoot);
|
|
159
|
+
const project = await loadProjectConfig(projectRoot);
|
|
160
|
+
const loadRegistry = dependencies.loadRegistry ?? loadGeneratedProjectRegistry;
|
|
161
|
+
await loadRegistry(projectRoot).catch(() => void 0);
|
|
162
|
+
const registry = await prepareProjectDiscovery(projectRoot, project.config);
|
|
163
|
+
const broadcastModule = await loadModule(projectRoot);
|
|
164
|
+
const realtime = await createRealtimeWorkerBindings(projectRoot);
|
|
165
|
+
const worker = await broadcastModule.startBroadcastWorker({
|
|
166
|
+
config: config.broadcast,
|
|
167
|
+
queue: config.queue,
|
|
168
|
+
...hasLoadedRedisConfigSection(config.loadedFiles) ? { redis: config.redis } : {},
|
|
169
|
+
...registry ? {
|
|
170
|
+
channelAuth: {
|
|
171
|
+
registry: {
|
|
172
|
+
projectRoot,
|
|
173
|
+
channels: registry.channels
|
|
174
|
+
},
|
|
175
|
+
importModule: async (absolutePath) => await importProjectModule(projectRoot, absolutePath)
|
|
176
|
+
}
|
|
177
|
+
} : {},
|
|
178
|
+
...realtime ? { realtime } : {}
|
|
179
|
+
});
|
|
180
|
+
writeLine(io.stdout, `[broadcast] Worker listening on ${worker.host}:${worker.port}`);
|
|
181
|
+
await new Promise((resolvePromise) => {
|
|
182
|
+
let stopped = false;
|
|
183
|
+
const stop = async () => {
|
|
184
|
+
if (stopped) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
stopped = true;
|
|
188
|
+
process.off("SIGINT", onSignal);
|
|
189
|
+
process.off("SIGTERM", onSignal);
|
|
190
|
+
await worker.stop();
|
|
191
|
+
resolvePromise();
|
|
192
|
+
};
|
|
193
|
+
const onSignal = () => {
|
|
194
|
+
void stop();
|
|
195
|
+
};
|
|
196
|
+
process.on("SIGINT", onSignal);
|
|
197
|
+
process.on("SIGTERM", onSignal);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
export {
|
|
201
|
+
loadBroadcastCliModule,
|
|
202
|
+
runBroadcastWorkCommand
|
|
203
|
+
};
|