@holo-js/cli 0.1.9 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/bin/holo.mjs +179 -81
  2. package/dist/broadcast-III5MB3R.mjs +203 -0
  3. package/dist/broadcast-ZIFYFOUQ.mjs +203 -0
  4. package/dist/{cache-ETOIQ5IG.mjs → cache-634WUR3T.mjs} +6 -6
  5. package/dist/cache-7J7DIOP6.mjs +66 -0
  6. package/dist/{cache-migrations-2GGI4TJK.mjs → cache-migrations-2NBEUF2T.mjs} +50 -30
  7. package/dist/cache-migrations-S2LJMDOQ.mjs +173 -0
  8. package/dist/{chunk-WRZFATUT.mjs → chunk-4OHJC3GL.mjs} +232 -143
  9. package/dist/{chunk-ASTSSSL2.mjs → chunk-5TEH2QPK.mjs} +99 -122
  10. package/dist/{chunk-F4MT6GBK.mjs → chunk-FGQ2I2YH.mjs} +1 -1
  11. package/dist/chunk-I7QBCEV7.mjs +33 -0
  12. package/dist/{chunk-R6BWRY3E.mjs → chunk-ILU426CF.mjs} +3 -1
  13. package/dist/{chunk-IMOGEKB4.mjs → chunk-J76GH2DR.mjs} +229 -119
  14. package/dist/{chunk-HB4Q7VYK.mjs → chunk-KS5TWO75.mjs} +30 -273
  15. package/dist/{chunk-57SJ566R.mjs → chunk-LBJAJLKU.mjs} +1 -1
  16. package/dist/{chunk-BAFQ2GOA.mjs → chunk-LXGQCG56.mjs} +1 -1
  17. package/dist/chunk-MCVRN7KX.mjs +3308 -0
  18. package/dist/chunk-SFRAGRHY.mjs +472 -0
  19. package/dist/{chunk-7JR73TOH.mjs → chunk-VP2E62DF.mjs} +36 -25
  20. package/dist/{chunk-5EU32E7X.mjs → chunk-VRGB6DIS.mjs} +116 -12
  21. package/dist/{chunk-SRPGIWCF.mjs → chunk-YEFJBN56.mjs} +2 -2
  22. package/dist/{config-ARLE6PKR.mjs → config-MD27U4FM.mjs} +3 -3
  23. package/dist/{dev-6RG5SSZ7.mjs → dev-M2GGURAX.mjs} +9 -7
  24. package/dist/dev-PBNFQK6Y.mjs +44 -0
  25. package/dist/{discovery-FCVGQQVD.mjs → discovery-GWTBF5RZ.mjs} +3 -3
  26. package/dist/{generators-UI2LJK3O.mjs → generators-BZJ53PUU.mjs} +13 -36
  27. package/dist/generators-DEPLONDJ.mjs +520 -0
  28. package/dist/index.mjs +181 -83
  29. package/dist/{media-migrations-JQSDCC7S.mjs → media-migrations-5EISZBSD.mjs} +9 -20
  30. package/dist/media-migrations-NMUWBEKE.mjs +106 -0
  31. package/dist/{queue-BY3PLH4I.mjs → queue-FRAVPNFJ.mjs} +12 -12
  32. package/dist/queue-GY7BWGTX.mjs +625 -0
  33. package/dist/{queue-migrations-YZUKEZK7.mjs → queue-migrations-J7YPIKRB.mjs} +13 -12
  34. package/dist/queue-migrations-O24ERNFF.mjs +167 -0
  35. package/dist/{runtime-BI343WHS.mjs → runtime-2AA7ZLJ6.mjs} +9 -9
  36. package/dist/{runtime-ZKD6URAV.mjs → runtime-GSXF4NB3.mjs} +1 -1
  37. package/dist/runtime-HGK2MWSC.mjs +57 -0
  38. package/dist/runtime-worker.d.ts +2 -0
  39. package/dist/runtime-worker.mjs +276 -0
  40. package/dist/{scaffold-UBOS2NZR.mjs → scaffold-DEOTRALR.mjs} +9 -5
  41. package/dist/scaffold-Y232IGYS.mjs +139 -0
  42. package/dist/{security-TYPVOYGF.mjs → security-MZW2CJKS.mjs} +6 -6
  43. package/dist/security-XVG673UR.mjs +71 -0
  44. package/package.json +9 -7
  45. package/dist/broadcast-VR46UZEL.mjs +0 -84
  46. package/dist/chunk-ZXDU7RHU.mjs +0 -9
@@ -7,6 +7,7 @@ import {
7
7
  GENERATED_BROADCAST_PATH,
8
8
  GENERATED_BROADCAST_TYPES_PATH,
9
9
  GENERATED_CHANNELS_PATH,
10
+ GENERATED_CHANNEL_IMPORTER_PATH,
10
11
  GENERATED_COMMANDS_PATH,
11
12
  GENERATED_CONFIG_TYPES_PATH,
12
13
  GENERATED_EVENTS_PATH,
@@ -32,7 +33,7 @@ import {
32
33
  isRecord,
33
34
  makeProjectRelativePath,
34
35
  normalizeScaffoldOptionalPackages
35
- } from "./chunk-R6BWRY3E.mjs";
36
+ } from "./chunk-ILU426CF.mjs";
36
37
 
37
38
  // src/templates.ts
38
39
  import { dirname, relative, resolve } from "path";
@@ -415,31 +416,10 @@ function renderNuxtHostedAuthCallbackRoute(spec) {
415
416
  }
416
417
  function renderNuxtHostedAuthLogoutRoute(spec) {
417
418
  return [
418
- "import { provider } from '@holo-js/auth'",
419
419
  `import { ${spec.logoutFunction} } from '${spec.packageName}'`,
420
- "import { createError, sendRedirect } from 'h3'",
421
420
  "",
422
421
  "export default defineEventHandler(async (event) => {",
423
- " let currentProvider: string | null",
424
- " try {",
425
- " currentProvider = await provider()",
426
- " } catch {",
427
- " return await sendRedirect(event, '/', 303)",
428
- " }",
429
- "",
430
- ` if (currentProvider !== '${spec.provider}') {`,
431
- " return await sendRedirect(event, '/', 303)",
432
- " }",
433
- "",
434
- ` const { data, error } = await ${spec.logoutFunction}(event)`,
435
- " if (error) {",
436
- " throw createError({",
437
- " statusCode: error.status,",
438
- " statusMessage: error.message,",
439
- " })",
440
- " }",
441
- "",
442
- " return await sendRedirect(event, data.url, 303)",
422
+ ` return await ${spec.logoutFunction}(event)`,
443
423
  "})",
444
424
  ""
445
425
  ].join("\n");
@@ -631,30 +611,13 @@ function renderNextHostedAuthLogoutRoute(spec) {
631
611
  }
632
612
  function renderNextGeneratedHostedAuthLogoutRoute(spec) {
633
613
  return [
634
- "import { provider } from '@holo-js/auth'",
635
614
  `import { ${spec.logoutFunction} } from '${spec.packageName}'`,
636
615
  "import { holo } from './holo'",
637
616
  "",
638
617
  "export async function POST(request: Request) {",
639
618
  " await holo.getApp()",
640
619
  "",
641
- " let currentProvider: string | null",
642
- " try {",
643
- " currentProvider = await provider()",
644
- " } catch {",
645
- " return Response.redirect(new URL('/', request.url), 303)",
646
- " }",
647
- "",
648
- ` if (currentProvider !== '${spec.provider}') {`,
649
- " return Response.redirect(new URL('/', request.url), 303)",
650
- " }",
651
- "",
652
- ` const { data, error } = await ${spec.logoutFunction}(request)`,
653
- " if (error) {",
654
- " return Response.json({ data, error }, { status: error.status })",
655
- " }",
656
- "",
657
- " return Response.redirect(data.url, 303)",
620
+ ` return await ${spec.logoutFunction}(request)`,
658
621
  "}",
659
622
  ""
660
623
  ].join("\n");
@@ -686,34 +649,74 @@ function renderNextGeneratedStorageRoute() {
686
649
  function renderNextBroadcastAuthRoute() {
687
650
  return renderNextRouteBridge("../../../.holo-js/generated/next/broadcast-auth-route", ["POST"]);
688
651
  }
652
+ function renderNextBroadcastConfigRoute() {
653
+ return renderNextRouteBridge("../../../.holo-js/generated/next/broadcast-config-route", ["GET"]);
654
+ }
655
+ function renderNextGeneratedBroadcastConfigRoute() {
656
+ return [
657
+ "import { renderBroadcastClientConfigResponse } from '@holo-js/broadcast/client-config'",
658
+ "import { holo } from './holo'",
659
+ "",
660
+ "export async function GET() {",
661
+ " const app = await holo.getApp()",
662
+ " return renderBroadcastClientConfigResponse(app.config.broadcast)",
663
+ "}",
664
+ ""
665
+ ].join("\n");
666
+ }
689
667
  function renderNextGeneratedBroadcastAuthRoute() {
690
668
  return [
691
669
  "import { renderBroadcastAuthResponse } from '@holo-js/broadcast/auth'",
670
+ "import { importBroadcastChannelModule } from '../channel-importer'",
692
671
  "import { holo } from './holo'",
693
672
  "",
694
673
  "export async function POST(request: Request) {",
695
674
  " const app = await holo.getApp()",
696
675
  " const auth = await holo.getAuth()",
676
+ " const connection = app.config.broadcast.connections[app.config.broadcast.default]",
677
+ " const signing = connection?.driver === 'holo' && 'key' in connection && 'secret' in connection",
678
+ " ? {",
679
+ " appKey: connection.key,",
680
+ " appSecret: connection.secret,",
681
+ " }",
682
+ " : {}",
697
683
  "",
698
684
  " return await renderBroadcastAuthResponse(request, {",
699
- " resolveUser: async () => await auth?.user(),",
685
+ " ...signing,",
686
+ " resolveUser: async (_request, context) => {",
687
+ " const guardAuth = context.guard ? auth?.guard(context.guard) : undefined",
688
+ " return guardAuth ? await guardAuth.user() : await auth?.user()",
689
+ " },",
700
690
  " channelAuth: {",
701
691
  " registry: {",
702
692
  " projectRoot: app.projectRoot,",
703
693
  " channels: app.registry?.channels ?? [],",
704
694
  " },",
695
+ " importModule: importBroadcastChannelModule,",
705
696
  " },",
706
697
  " })",
707
698
  "}",
708
699
  ""
709
700
  ].join("\n");
710
701
  }
702
+ function renderNextGeneratedRealtimeDefinitions(importPaths = []) {
703
+ const imports = importPaths.map((importPath, index) => `import * as realtimeModule${index} from ${JSON.stringify(importPath)}`);
704
+ const values = importPaths.map((_importPath, index) => `...Object.values(realtimeModule${index})`);
705
+ return [
706
+ ...imports,
707
+ "",
708
+ `export const realtimeDefinitions = [${values.join(", ")}] as const`,
709
+ ""
710
+ ].join("\n");
711
+ }
711
712
  function renderNextManagedRouteFiles(options = {}) {
712
713
  return [
713
714
  { path: ".holo-js/generated/next/holo.ts", contents: renderNextHoloHelper() },
714
715
  { path: ".holo-js/generated/next/bootstrap.mjs", contents: renderNextRuntimeBootstrap() },
715
716
  ...options.storageEnabled ? [{ path: ".holo-js/generated/next/storage-route.ts", contents: renderNextGeneratedStorageRoute() }] : [],
716
- ...options.broadcastAuthEnabled ? [{ path: ".holo-js/generated/next/broadcast-auth-route.ts", contents: renderNextGeneratedBroadcastAuthRoute() }] : []
717
+ ...options.broadcastEnabled ? [{ path: ".holo-js/generated/next/broadcast-config-route.ts", contents: renderNextGeneratedBroadcastConfigRoute() }] : [],
718
+ ...options.broadcastAuthEnabled ? [{ path: ".holo-js/generated/next/broadcast-auth-route.ts", contents: renderNextGeneratedBroadcastAuthRoute() }] : [],
719
+ ...options.realtimeEnabled ? [{ path: ".holo-js/generated/next/realtime-definitions.ts", contents: renderNextGeneratedRealtimeDefinitions() }] : []
717
720
  ];
718
721
  }
719
722
  function renderNextManagedHostedAuthRouteFiles(features) {
@@ -770,7 +773,7 @@ function renderSvelteServerUserHooks() {
770
773
  ""
771
774
  ].join("\n");
772
775
  }
773
- function renderSvelteViteConfig(_storageEnabled) {
776
+ function renderSvelteViteConfig(_storageEnabled, realtimeEnabled = false) {
774
777
  const externals = [
775
778
  " '@holo-js/adapter-sveltekit',",
776
779
  " '@holo-js/auth',",
@@ -798,6 +801,7 @@ function renderSvelteViteConfig(_storageEnabled) {
798
801
  " '@holo-js/queue',",
799
802
  " '@holo-js/queue-db',",
800
803
  " '@holo-js/queue-redis',",
804
+ " '@holo-js/realtime',",
801
805
  " '@holo-js/security',",
802
806
  " '@holo-js/session',",
803
807
  " '@holo-js/storage',",
@@ -811,10 +815,11 @@ function renderSvelteViteConfig(_storageEnabled) {
811
815
  ];
812
816
  return [
813
817
  "import { sveltekit } from '@sveltejs/kit/vite'",
818
+ ...realtimeEnabled ? ["import { holoSvelteKitRealtime } from '@holo-js/adapter-sveltekit/vite'"] : [],
814
819
  "import { defineConfig } from 'vite'",
815
820
  "",
816
821
  "export default defineConfig({",
817
- " plugins: [sveltekit()],",
822
+ ` plugins: [${realtimeEnabled ? "holoSvelteKitRealtime(), " : ""}sveltekit()],`,
818
823
  " server: {",
819
824
  " fs: {",
820
825
  " allow: ['.holo-js/generated'],",
@@ -933,28 +938,11 @@ function renderSvelteHostedAuthCallbackRoute(spec) {
933
938
  }
934
939
  function renderSvelteHostedAuthLogoutRoute(spec) {
935
940
  return [
936
- "import { redirect, type RequestEvent } from '@sveltejs/kit'",
937
- "import { provider } from '@holo-js/auth'",
941
+ "import { type RequestEvent } from '@sveltejs/kit'",
938
942
  `import { ${spec.logoutFunction} } from '${spec.packageName}'`,
939
943
  "",
940
944
  "export async function POST(event: RequestEvent) {",
941
- " let currentProvider: string | null",
942
- " try {",
943
- " currentProvider = await provider()",
944
- " } catch {",
945
- " throw redirect(303, '/')",
946
- " }",
947
- "",
948
- ` if (currentProvider !== '${spec.provider}') {`,
949
- " throw redirect(303, '/')",
950
- " }",
951
- "",
952
- ` const { data, error } = await ${spec.logoutFunction}(event)`,
953
- " if (error) {",
954
- " return Response.json({ data, error }, { status: error.status })",
955
- " }",
956
- "",
957
- " throw redirect(303, data.url)",
945
+ ` return await ${spec.logoutFunction}(event)`,
958
946
  "}",
959
947
  ""
960
948
  ].join("\n");
@@ -965,8 +953,7 @@ function renderSvelteHostedAuthRouteFiles(provider) {
965
953
  { path: `src/routes/api/auth/${provider}/login/+server.ts`, contents: renderSvelteHostedAuthLoginRoute(spec) },
966
954
  { path: `src/routes/api/auth/${provider}/register/+server.ts`, contents: renderSvelteHostedAuthRegisterRoute(spec) },
967
955
  { path: `src/routes/api/auth/${provider}/callback/+server.ts`, contents: renderSvelteHostedAuthCallbackRoute(spec) },
968
- { path: `src/routes/api/auth/${provider}/logout/+server.ts`, contents: renderSvelteHostedAuthLogoutRoute(spec) },
969
- ...renderSvelteManagedRuntimeFiles()
956
+ { path: `src/routes/api/auth/${provider}/logout/+server.ts`, contents: renderSvelteHostedAuthLogoutRoute(spec) }
970
957
  ];
971
958
  }
972
959
  function renderAuthProviderRouteFiles(framework, features) {
@@ -993,14 +980,14 @@ function renderAuthRouteFiles(framework) {
993
980
  { path: "server/api/auth/user.get.ts", contents: renderNuxtCurrentAuthRoute() }
994
981
  ];
995
982
  }
996
- return [
997
- ...renderSvelteManagedRuntimeFiles()
998
- ];
983
+ return [];
999
984
  }
1000
985
  function renderFrameworkFiles(options) {
1001
986
  const optionalPackages = normalizeScaffoldOptionalPackages(options.optionalPackages);
1002
987
  const storageEnabled = optionalPackages.includes("storage");
1003
988
  const authEnabled = optionalPackages.includes("auth");
989
+ const broadcastEnabled = optionalPackages.includes("broadcast");
990
+ const realtimeEnabled = optionalPackages.includes("realtime");
1004
991
  if (options.framework === "nuxt") {
1005
992
  return [
1006
993
  { path: "app/app.vue", contents: renderNuxtAppVue(options.projectName) },
@@ -1017,12 +1004,13 @@ function renderFrameworkFiles(options) {
1017
1004
  { path: "app/page.tsx", contents: renderNextPage(options.projectName) },
1018
1005
  ...authEnabled ? [{ path: "app/api/auth/user/route.ts", contents: renderNextCurrentAuthRoute() }] : [],
1019
1006
  ...storageEnabled ? [{ path: "app/storage/[[...path]]/route.ts", contents: renderNextStorageRoute() }] : [],
1020
- ...renderNextManagedRouteFiles({ authEnabled, storageEnabled })
1007
+ ...broadcastEnabled ? [{ path: "app/broadcasting/config/route.ts", contents: renderNextBroadcastConfigRoute() }] : [],
1008
+ ...renderNextManagedRouteFiles({ authEnabled, broadcastEnabled, storageEnabled, realtimeEnabled })
1021
1009
  ];
1022
1010
  }
1023
1011
  return [
1024
1012
  { path: "svelte.config.js", contents: renderSvelteConfig() },
1025
- { path: "vite.config.ts", contents: renderSvelteViteConfig(storageEnabled) },
1013
+ { path: "vite.config.ts", contents: renderSvelteViteConfig(storageEnabled, realtimeEnabled) },
1026
1014
  { path: "src/hooks.ts", contents: renderSvelteUserHooks() },
1027
1015
  { path: "src/hooks.server.ts", contents: renderSvelteServerUserHooks() },
1028
1016
  { path: "src/app.html", contents: renderSvelteAppHtml() },
@@ -1047,17 +1035,7 @@ function renderFrameworkRunner(options) {
1047
1035
  "const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))",
1048
1036
  "const framework = String(manifest.framework ?? '')",
1049
1037
  `const commandName = ${JSON.stringify(commandName)}`,
1050
- "const commandArgs = mode === 'dev'",
1051
- " ? ['dev']",
1052
- " : mode === 'build'",
1053
- " ? framework === 'sveltekit' ? ['build', '--logLevel', 'error'] : ['build']",
1054
- " : undefined",
1055
- "",
1056
- "if (!commandArgs) {",
1057
- " console.error(`[holo] Unknown framework runner mode: ${String(mode)}`)",
1058
- " process.exit(1)",
1059
- "}",
1060
- "",
1038
+ "const passthroughArgs = process.argv.slice(3)",
1061
1039
  "const binaryPath = resolve(",
1062
1040
  " projectRoot,",
1063
1041
  " 'node_modules',",
@@ -1065,6 +1043,39 @@ function renderFrameworkRunner(options) {
1065
1043
  " process.platform === 'win32' ? `${commandName}.cmd` : commandName,",
1066
1044
  ")",
1067
1045
  "",
1046
+ "function resolveCommandInvocation() {",
1047
+ " if (mode === 'dev') {",
1048
+ " return { command: binaryPath, args: ['dev'] }",
1049
+ " }",
1050
+ "",
1051
+ " if (mode === 'build') {",
1052
+ " return { command: binaryPath, args: framework === 'sveltekit' ? ['build', '--logLevel', 'error'] : ['build'] }",
1053
+ " }",
1054
+ "",
1055
+ " if (mode === 'start') {",
1056
+ " if (framework === 'next') {",
1057
+ " return { command: binaryPath, args: ['start', ...passthroughArgs] }",
1058
+ " }",
1059
+ "",
1060
+ " if (framework === 'nuxt') {",
1061
+ " return { command: process.execPath, args: [resolve(projectRoot, '.output/server/index.mjs'), ...passthroughArgs] }",
1062
+ " }",
1063
+ "",
1064
+ " if (framework === 'sveltekit') {",
1065
+ " return { command: process.execPath, args: [resolve(projectRoot, 'build/index.js'), ...passthroughArgs] }",
1066
+ " }",
1067
+ " }",
1068
+ "",
1069
+ " return undefined",
1070
+ "}",
1071
+ "",
1072
+ "const commandInvocation = resolveCommandInvocation()",
1073
+ "",
1074
+ "if (!commandInvocation) {",
1075
+ " console.error(`[holo] Unknown framework runner mode: ${String(mode)}`)",
1076
+ " process.exit(1)",
1077
+ "}",
1078
+ "",
1068
1079
  "const suppressedOutput = framework === 'sveltekit'",
1069
1080
  " ? new Set([",
1070
1081
  ` '"try_get_request_store" is imported from external module "@sveltejs/kit/internal/server" but never used in ".svelte-kit/adapter-node/index.js".',`,
@@ -1221,11 +1232,16 @@ function renderFrameworkRunner(options) {
1221
1232
  " return waitForProcessExit(pid)",
1222
1233
  "}",
1223
1234
  "",
1224
- "if (!existsSync(binaryPath)) {",
1235
+ "if ((mode !== 'start' || framework === 'next') && !existsSync(binaryPath)) {",
1225
1236
  ' console.error(`[holo] Missing framework binary "${commandName}" for "${framework}". Run your package manager install first.`)',
1226
1237
  " process.exit(1)",
1227
1238
  "}",
1228
1239
  "",
1240
+ "if (mode === 'start' && framework !== 'next' && !existsSync(commandInvocation.args[0])) {",
1241
+ ' console.error(`[holo] Missing production server entry "${commandInvocation.args[0]}". Run "holo build" before "holo start".`)',
1242
+ " process.exit(1)",
1243
+ "}",
1244
+ "",
1229
1245
  "let child = null",
1230
1246
  "let forwardedSignal = null",
1231
1247
  "",
@@ -1263,6 +1279,9 @@ function renderFrameworkRunner(options) {
1263
1279
  " while (true) {",
1264
1280
  " const stderrLines = []",
1265
1281
  " const childEnv = { ...process.env }",
1282
+ " if (mode === 'build') {",
1283
+ " childEnv.HOLO_INTERNAL_FRAMEWORK_BUILD = '1'",
1284
+ " }",
1266
1285
  " const preloads = [runtimeSchemaPath]",
1267
1286
  " if (framework === 'next') {",
1268
1287
  " preloads.push(nextRuntimeBootstrapPath)",
@@ -1276,7 +1295,7 @@ function renderFrameworkRunner(options) {
1276
1295
  " ? `${childEnv.NODE_OPTIONS} ${preload}`",
1277
1296
  " : preload",
1278
1297
  " }",
1279
- " child = spawn(binaryPath, commandArgs, {",
1298
+ " child = spawn(commandInvocation.command, commandInvocation.args, {",
1280
1299
  " cwd: projectRoot,",
1281
1300
  " env: childEnv,",
1282
1301
  " stdio: ['inherit', 'pipe', 'pipe'],",
@@ -1335,13 +1354,13 @@ function renderFrameworkRunner(options) {
1335
1354
  // src/project/registry.ts
1336
1355
  import { constants as fsConstants } from "fs";
1337
1356
  import { access, mkdir as mkdir2, readFile as readFile2, readdir as readdir2, writeFile as writeFile2 } from "fs/promises";
1338
- import { dirname as dirname3, extname, join, resolve as resolve3 } from "path";
1357
+ import { dirname as dirname3, extname as extname2, join, resolve as resolve3 } from "path";
1339
1358
  import { loadConfigDirectory } from "@holo-js/config";
1340
1359
  import { DEFAULT_HOLO_PROJECT_PATHS, renderGeneratedSchemaRuntimeModule } from "@holo-js/db";
1341
1360
 
1342
1361
  // src/project/registry-svelte.ts
1343
- import { mkdir, readdir, readFile, rm, writeFile } from "fs/promises";
1344
- import { dirname as dirname2, resolve as resolve2 } from "path";
1362
+ import { mkdir, readdir, readFile, rm, stat, writeFile } from "fs/promises";
1363
+ import { dirname as dirname2, extname, relative as relative2, resolve as resolve2, sep } from "path";
1345
1364
  function renderManagedSvelteHooksModule() {
1346
1365
  return [
1347
1366
  "// Generated by holo prepare. Do not edit.",
@@ -1395,7 +1414,10 @@ function renderManagedSvelteServerHooksModule(features) {
1395
1414
  "import { adapterSvelteKitInternals, runWithSvelteKitRequestEvent } from '@holo-js/adapter-sveltekit'",
1396
1415
  "import { sequence } from '@sveltejs/kit/hooks'",
1397
1416
  ...features.authEnabled ? ["import auth, { check, isAuthError, provider, user } from '@holo-js/auth'"] : [],
1398
- ...features.broadcastEnabled ? ["import { renderBroadcastAuthResponse } from '@holo-js/broadcast/auth'"] : [],
1417
+ ...features.broadcastEnabled ? [
1418
+ "import { renderBroadcastAuthResponse } from '@holo-js/broadcast/auth'",
1419
+ "import { renderBroadcastClientConfigResponse } from '@holo-js/broadcast/client-config'"
1420
+ ] : [],
1399
1421
  ...features.storageEnabled ? ["import { createPublicStorageResponse } from '@holo-js/storage'"] : [],
1400
1422
  ...features.clerkEnabled ? ["import { completeClerkAuth, loginWithClerk, logoutWithClerk, registerWithClerk } from '@holo-js/auth-clerk'"] : [],
1401
1423
  ...features.workosEnabled ? ["import { completeWorkosAuth, loginWithWorkos, logoutWithWorkos, registerWithWorkos } from '@holo-js/auth-workos'"] : [],
@@ -1406,7 +1428,7 @@ function renderManagedSvelteServerHooksModule(features) {
1406
1428
  const routeHandlers = [
1407
1429
  ...features.authEnabled ? ["handleHoloCurrentAuthRoute"] : [],
1408
1430
  ...features.storageEnabled ? ["handleHoloStorageRoute"] : [],
1409
- ...features.broadcastEnabled ? ["handleHoloBroadcastAuthRoute"] : [],
1431
+ ...features.broadcastEnabled ? ["handleHoloBroadcastConfigRoute", "handleHoloBroadcastAuthRoute"] : [],
1410
1432
  ...features.clerkEnabled ? [
1411
1433
  "handleHoloClerkLoginRoute",
1412
1434
  "handleHoloClerkRegisterRoute",
@@ -1499,14 +1521,33 @@ function renderManagedSvelteServerHooksModule(features) {
1499
1521
  ""
1500
1522
  ] : [],
1501
1523
  ...features.broadcastEnabled ? [
1524
+ "async function handleHoloBroadcastConfigRoute(event: RequestEvent, app: HoloApp): Promise<Response | undefined> {",
1525
+ " if (!isRoute(event, 'GET', ['/broadcasting/config'])) {",
1526
+ " return undefined",
1527
+ " }",
1528
+ "",
1529
+ " return renderBroadcastClientConfigResponse(app.config.broadcast)",
1530
+ "}",
1531
+ "",
1502
1532
  "async function handleHoloBroadcastAuthRoute(event: RequestEvent, app: HoloApp): Promise<Response | undefined> {",
1503
1533
  " if (!isRoute(event, 'POST', ['/broadcasting/auth'])) {",
1504
1534
  " return undefined",
1505
1535
  " }",
1506
1536
  "",
1507
1537
  " const auth = await holo.getAuth()",
1538
+ " const connection = app.config.broadcast.connections[app.config.broadcast.default]",
1539
+ " const signing = connection?.driver === 'holo' && 'key' in connection && 'secret' in connection",
1540
+ " ? {",
1541
+ " appKey: connection.key,",
1542
+ " appSecret: connection.secret,",
1543
+ " }",
1544
+ " : {}",
1508
1545
  " return await renderBroadcastAuthResponse(event.request, {",
1509
- " resolveUser: async () => await auth?.user(),",
1546
+ " ...signing,",
1547
+ " resolveUser: async (_request, context) => {",
1548
+ " const guardAuth = context.guard ? auth?.guard(context.guard) : undefined",
1549
+ " return guardAuth ? await guardAuth.user() : await auth?.user()",
1550
+ " },",
1510
1551
  " channelAuth: {",
1511
1552
  " registry: {",
1512
1553
  " projectRoot: app.projectRoot,",
@@ -1634,23 +1675,7 @@ function renderSvelteHostedAuthHookRoutes(providerName, functionSuffix) {
1634
1675
  " return undefined",
1635
1676
  " }",
1636
1677
  "",
1637
- " let currentProvider: string | null",
1638
- " try {",
1639
- " currentProvider = await provider()",
1640
- " } catch {",
1641
- " return redirectResponse(event, '/')",
1642
- " }",
1643
- "",
1644
- ` if (currentProvider !== '${providerName}') {`,
1645
- " return redirectResponse(event, '/')",
1646
- " }",
1647
- "",
1648
- ` const { data, error } = await logoutWith${functionSuffix}(event)`,
1649
- " if (error) {",
1650
- " return Response.json({ data, error }, { status: error.status })",
1651
- " }",
1652
- "",
1653
- " return redirectResponse(event, data.url)",
1678
+ ` return await logoutWith${functionSuffix}(event)`,
1654
1679
  "}",
1655
1680
  ""
1656
1681
  ];
@@ -1723,6 +1748,9 @@ async function unlinkIfPresent(path) {
1723
1748
  async function pathExists(path) {
1724
1749
  return await readFileIfPresent(path) !== void 0;
1725
1750
  }
1751
+ async function directoryExists(path) {
1752
+ return await stat(path).then((entry) => entry.isDirectory(), () => false);
1753
+ }
1726
1754
  var SVELTE_HOOKS_OVERRIDE_BLOCK = [
1727
1755
  " files: {",
1728
1756
  " hooks: {",
@@ -1883,17 +1911,43 @@ async function directoryContainsText(path, pattern) {
1883
1911
  }
1884
1912
  return false;
1885
1913
  }
1914
+ var realtimeDefinitionExtensions = /* @__PURE__ */ new Set([".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"]);
1915
+ async function collectRealtimeDefinitionFiles(root) {
1916
+ const entries = await readdir(root, { withFileTypes: true }).catch(() => []);
1917
+ const files = await Promise.all(entries.map(async (entry) => {
1918
+ const entryPath = resolve2(root, entry.name);
1919
+ if (entry.isDirectory()) {
1920
+ return await collectRealtimeDefinitionFiles(entryPath);
1921
+ }
1922
+ return realtimeDefinitionExtensions.has(extname(entry.name)) ? [entryPath] : [];
1923
+ }));
1924
+ return files.flat().sort((left, right) => left.localeCompare(right));
1925
+ }
1926
+ async function renderNextRealtimeDefinitions(projectRoot) {
1927
+ const generatedRoot = resolve2(projectRoot, ".holo-js/generated/next");
1928
+ const files = await collectRealtimeDefinitionFiles(resolve2(projectRoot, "server/realtime"));
1929
+ const importPaths = files.map((filePath) => {
1930
+ const withoutExtension = filePath.slice(0, -extname(filePath).length);
1931
+ const importPath = relative2(generatedRoot, withoutExtension).split(sep).join("/");
1932
+ return importPath.startsWith(".") ? importPath : `./${importPath}`;
1933
+ });
1934
+ return renderNextGeneratedRealtimeDefinitions(importPaths);
1935
+ }
1886
1936
  async function ensureNextManagedRoutes(projectRoot) {
1887
1937
  const authEnabled = await pathExists(resolve2(projectRoot, "app/api/auth/user/route.ts"));
1888
1938
  const storageEnabled = await pathExists(resolve2(projectRoot, "app/storage/[[...path]]/route.ts"));
1939
+ const broadcastEnabled = await pathExists(resolve2(projectRoot, "app/broadcasting/config/route.ts"));
1889
1940
  const broadcastAuthEnabled = await pathExists(resolve2(projectRoot, "app/broadcasting/auth/route.ts"));
1941
+ const realtimeEnabled = await directoryExists(resolve2(projectRoot, "server/realtime"));
1890
1942
  const clerkEnabled = await pathExists(resolve2(projectRoot, "app/api/auth/clerk/login/route.ts"));
1891
1943
  const workosEnabled = await pathExists(resolve2(projectRoot, "app/api/auth/workos/login/route.ts"));
1892
1944
  const files = [
1893
1945
  ...renderNextManagedRouteFiles({
1894
1946
  authEnabled,
1947
+ broadcastEnabled,
1895
1948
  storageEnabled,
1896
- broadcastAuthEnabled
1949
+ broadcastAuthEnabled,
1950
+ realtimeEnabled
1897
1951
  }),
1898
1952
  ...renderNextManagedHostedAuthRouteFiles({
1899
1953
  clerk: clerkEnabled,
@@ -1903,6 +1957,12 @@ async function ensureNextManagedRoutes(projectRoot) {
1903
1957
  for (const file of files) {
1904
1958
  await writeFileIfChanged(resolve2(projectRoot, file.path), file.contents);
1905
1959
  }
1960
+ if (realtimeEnabled) {
1961
+ await writeFileIfChanged(
1962
+ resolve2(projectRoot, ".holo-js/generated/next/realtime-definitions.ts"),
1963
+ await renderNextRealtimeDefinitions(projectRoot)
1964
+ );
1965
+ }
1906
1966
  await removeLegacyManagedHoloHelper(resolve2(projectRoot, "server/holo.ts"), renderNextHoloHelper());
1907
1967
  }
1908
1968
  function isRecord2(value) {
@@ -2137,8 +2197,8 @@ function renderAugmentationInterface(name, members) {
2137
2197
  ];
2138
2198
  }
2139
2199
  function renderGeneratedBroadcastTypes(broadcast, channels) {
2140
- const typedBroadcastEntries = broadcast.filter((entry) => [".ts", ".mts", ".cts"].includes(extname(entry.sourcePath)));
2141
- const typedChannelEntries = channels.filter((entry) => [".ts", ".mts", ".cts"].includes(extname(entry.sourcePath)));
2200
+ const typedBroadcastEntries = broadcast.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2201
+ const typedChannelEntries = channels.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2142
2202
  const broadcastImportNameByName = new Map(typedBroadcastEntries.map((entry, index) => [entry.name, `holoBroadcastModule${index}`]));
2143
2203
  const channelImportNameByPattern = new Map(typedChannelEntries.map((entry, index) => [entry.pattern, `holoChannelModule${index}`]));
2144
2204
  const broadcastNeedsDefinitionType = broadcast.some((entry) => !broadcastImportNameByName.get(entry.name) || !entry.exportName);
@@ -2220,6 +2280,7 @@ function renderGeneratedBroadcastManifest(registry) {
2220
2280
  ` name: ${JSON.stringify(entry.pattern)},`,
2221
2281
  ` pattern: ${JSON.stringify(entry.pattern)},`,
2222
2282
  ` type: ${JSON.stringify(entry.type)},`,
2283
+ ...typeof entry.guard === "undefined" ? [] : [` guard: ${JSON.stringify(entry.guard)},`],
2223
2284
  ` params: ${JSON.stringify(entry.params)},`,
2224
2285
  ` whispers: ${JSON.stringify(entry.whispers)},`
2225
2286
  ];
@@ -2245,6 +2306,7 @@ function renderGeneratedBroadcastManifest(registry) {
2245
2306
  "type GeneratedBroadcastManifestChannel = {",
2246
2307
  " readonly name: string",
2247
2308
  " readonly pattern: string",
2309
+ " readonly guard?: string",
2248
2310
  " readonly type: 'private' | 'presence'",
2249
2311
  " readonly params: readonly string[]",
2250
2312
  " readonly whispers: readonly string[]",
@@ -2273,9 +2335,33 @@ function renderGeneratedBroadcastManifest(registry) {
2273
2335
  ""
2274
2336
  ].join("\n");
2275
2337
  }
2338
+ function renderGeneratedChannelImporter(channels) {
2339
+ const importableChannels = channels.filter((entry) => [".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"].includes(extname2(entry.sourcePath)));
2340
+ const imports = importableChannels.map((entry, index) => {
2341
+ return `import * as holoChannelModule${index} from '${relativeImportPath(GENERATED_CHANNEL_IMPORTER_PATH, entry.sourcePath)}'`;
2342
+ });
2343
+ const cases = importableChannels.flatMap((entry, index) => [
2344
+ ` if (normalizedPath.endsWith(${JSON.stringify(`/${entry.sourcePath}`)})) {`,
2345
+ ` return holoChannelModule${index}`,
2346
+ " }"
2347
+ ]);
2348
+ return [
2349
+ "// Generated by holo prepare. Do not edit.",
2350
+ "",
2351
+ ...imports,
2352
+ ...imports.length > 0 ? [""] : [],
2353
+ "export async function importBroadcastChannelModule(absolutePath: string): Promise<unknown> {",
2354
+ " const normalizedPath = absolutePath.replaceAll('\\\\', '/')",
2355
+ ...cases,
2356
+ "",
2357
+ ' throw new Error(`[@holo-js/broadcast] Broadcast channel module "${absolutePath}" was not generated for this app.`)',
2358
+ "}",
2359
+ ""
2360
+ ].join("\n");
2361
+ }
2276
2362
  function renderGeneratedEventTypes(events, listeners) {
2277
- const typedEventEntries = events.filter((entry) => [".ts", ".mts", ".cts"].includes(extname(entry.sourcePath)));
2278
- const typedListenerEntries = listeners.filter((entry) => [".ts", ".mts", ".cts"].includes(extname(entry.sourcePath)));
2363
+ const typedEventEntries = events.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2364
+ const typedListenerEntries = listeners.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2279
2365
  const eventImportNameByName = new Map(typedEventEntries.map((entry, index) => [entry.name, `holoEventModule${index}`]));
2280
2366
  const listenerImportNameById = new Map(typedListenerEntries.map((entry, index) => [entry.id, `holoListenerModule${index}`]));
2281
2367
  const needsEventDefinitionType = events.some((entry) => !eventImportNameByName.get(entry.name) || !entry.exportName);
@@ -2328,7 +2414,7 @@ function renderGeneratedEventTypes(events, listeners) {
2328
2414
  ].join("\n");
2329
2415
  }
2330
2416
  function renderGeneratedQueueTypes(jobs) {
2331
- const typedJobs = jobs.filter((entry) => [".ts", ".mts", ".cts"].includes(extname(entry.sourcePath)));
2417
+ const typedJobs = jobs.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2332
2418
  const typeImportNameByJob = new Map(
2333
2419
  typedJobs.map((entry, index) => {
2334
2420
  return [entry.name, `holoQueueJobModule${index}`];
@@ -2364,7 +2450,23 @@ function renderGeneratedQueueTypes(jobs) {
2364
2450
  ""
2365
2451
  ].join("\n");
2366
2452
  }
2367
- function renderGeneratedAuthTypes(guards) {
2453
+ function getAuthProviderModelNames(auth) {
2454
+ if (!auth) {
2455
+ return [];
2456
+ }
2457
+ return [...new Set(Object.values(auth.guards).map((guard) => auth.providers[guard.provider]?.model).filter((model) => typeof model === "string" && model.length > 0))].sort((left, right) => left.localeCompare(right));
2458
+ }
2459
+ function renderGeneratedAuthTypes(auth, models = []) {
2460
+ const guards = auth?.guards ?? {};
2461
+ const modelByName = new Map(models.map((model) => [model.name, model]));
2462
+ const userModels = getAuthProviderModelNames(auth).map((modelName) => modelByName.get(modelName)).filter((model) => Boolean(model));
2463
+ const userTableNames = [...new Set(userModels.map((model) => model.tableName))];
2464
+ const userType = userModels.length > 0 ? userTableNames.map((tableName) => `InferSelect<GeneratedSchemaTable<${JSON.stringify(tableName)}>>`).join(" | ") : void 0;
2465
+ const imports = userType ? [
2466
+ "import type { AuthUserLike } from '@holo-js/auth'",
2467
+ "import type { GeneratedSchemaTable, InferSelect } from '@holo-js/db'",
2468
+ ""
2469
+ ] : [];
2368
2470
  const members = Object.entries(guards).sort(([left], [right]) => left.localeCompare(right)).filter((entry) => {
2369
2471
  return entry[1].driver === "session" || entry[1].driver === "token";
2370
2472
  }).map(([name, guard]) => {
@@ -2381,12 +2483,14 @@ function renderGeneratedAuthTypes(guards) {
2381
2483
  "// Generated by holo prepare. Do not edit.",
2382
2484
  "",
2383
2485
  "import '@holo-js/auth'",
2486
+ ...imports,
2384
2487
  "",
2385
2488
  "declare global {",
2386
2489
  " // eslint-disable-next-line @typescript-eslint/no-namespace",
2387
2490
  " namespace HoloAuth {",
2388
2491
  " interface TypeRegistry {",
2389
2492
  ...guardsMember,
2493
+ ...userType ? [` user: (${userType}) & AuthUserLike`] : [],
2390
2494
  " }",
2391
2495
  " }",
2392
2496
  "}",
@@ -2433,8 +2537,8 @@ function renderGeneratedAuthorizationRegistry(registry) {
2433
2537
  ].join("\n");
2434
2538
  }
2435
2539
  function renderGeneratedAuthorizationTypes(authorizationPolicies, authorizationAbilities, guardNames) {
2436
- const typedPolicyEntries = authorizationPolicies.filter((entry) => [".ts", ".mts", ".cts"].includes(extname(entry.sourcePath)));
2437
- const typedAbilityEntries = authorizationAbilities.filter((entry) => [".ts", ".mts", ".cts"].includes(extname(entry.sourcePath)));
2540
+ const typedPolicyEntries = authorizationPolicies.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2541
+ const typedAbilityEntries = authorizationAbilities.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2438
2542
  const policyImportNameByName = new Map(typedPolicyEntries.map((entry, index) => [entry.name, `holoAuthorizationPolicyModule${index}`]));
2439
2543
  const abilityImportNameByName = new Map(typedAbilityEntries.map((entry, index) => [entry.name, `holoAuthorizationAbilityModule${index}`]));
2440
2544
  const usesHoloAuthUser = authorizationPolicies.length > 0 || authorizationAbilities.length > 0 || guardNames.length > 0;
@@ -2526,7 +2630,7 @@ async function resolveGeneratedTsconfigBase(_projectRoot) {
2526
2630
  return "../../tsconfig.json";
2527
2631
  }
2528
2632
  function getConfigExtensionPriority(fileName) {
2529
- const extension = extname(fileName);
2633
+ const extension = extname2(fileName);
2530
2634
  const index = CONFIG_EXTENSION_PRIORITY.indexOf(extension);
2531
2635
  return index >= 0 ? index : Number.MAX_SAFE_INTEGER;
2532
2636
  }
@@ -2538,7 +2642,7 @@ async function collectProjectConfigEntries(projectRoot) {
2538
2642
  if (!entry.isFile()) {
2539
2643
  continue;
2540
2644
  }
2541
- const extension = extname(entry.name);
2645
+ const extension = extname2(entry.name);
2542
2646
  if (!SUPPORTED_CONFIG_EXTENSIONS.has(extension)) {
2543
2647
  continue;
2544
2648
  }
@@ -2635,9 +2739,10 @@ async function writeGeneratedProjectRegistry(projectRoot, registry) {
2635
2739
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_LISTENERS_PATH), renderGeneratedModule("listeners", nextRegistry.listeners));
2636
2740
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_BROADCAST_PATH), renderGeneratedModule("broadcast", nextRegistry.broadcast));
2637
2741
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_CHANNELS_PATH), renderGeneratedModule("channels", nextRegistry.channels));
2742
+ await writeFileIfChanged2(resolve3(projectRoot, GENERATED_CHANNEL_IMPORTER_PATH), renderGeneratedChannelImporter(nextRegistry.channels));
2638
2743
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_BROADCAST_MANIFEST_PATH), renderGeneratedBroadcastManifest(nextRegistry));
2639
2744
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_BROADCAST_TYPES_PATH), renderGeneratedBroadcastTypes(nextRegistry.broadcast, nextRegistry.channels));
2640
- await writeFileIfChanged2(resolve3(projectRoot, GENERATED_AUTH_TYPES_PATH), renderGeneratedAuthTypes(loadedConfig?.auth?.guards ?? {}));
2745
+ await writeFileIfChanged2(resolve3(projectRoot, GENERATED_AUTH_TYPES_PATH), renderGeneratedAuthTypes(loadedConfig?.auth, nextRegistry.models));
2641
2746
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_CONFIG_TYPES_PATH), renderGeneratedConfigTypes(projectRoot, configEntries));
2642
2747
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_EVENT_TYPES_PATH), renderGeneratedEventTypes(nextRegistry.events, nextRegistry.listeners));
2643
2748
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_QUEUE_TYPES_PATH), renderGeneratedQueueTypes(nextRegistry.jobs));
@@ -2719,9 +2824,14 @@ export {
2719
2824
  resolveNameInfo,
2720
2825
  resolveArtifactPath,
2721
2826
  renderNextHoloHelper,
2827
+ renderNextRuntimeBootstrap,
2722
2828
  renderNextBroadcastAuthRoute,
2829
+ renderNextBroadcastConfigRoute,
2830
+ renderNextGeneratedBroadcastConfigRoute,
2723
2831
  renderNextGeneratedBroadcastAuthRoute,
2832
+ renderNextGeneratedRealtimeDefinitions,
2724
2833
  renderNextManagedHostedAuthRouteFiles,
2834
+ renderSvelteViteConfig,
2725
2835
  renderSvelteHoloHelper,
2726
2836
  renderAuthProviderRouteFiles,
2727
2837
  renderAuthRouteFiles,