@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.
Files changed (46) hide show
  1. package/dist/bin/holo.mjs +165 -81
  2. package/dist/broadcast-WI6PJS5P.mjs +203 -0
  3. package/dist/broadcast-YWS4N5QU.mjs +203 -0
  4. package/dist/{cache-ETOIQ5IG.mjs → cache-KWNQECAA.mjs} +6 -6
  5. package/dist/cache-QARFSW4F.mjs +66 -0
  6. package/dist/{cache-migrations-2GGI4TJK.mjs → cache-migrations-3OXR4FN5.mjs} +50 -30
  7. package/dist/cache-migrations-MDFMDVTK.mjs +173 -0
  8. package/dist/{chunk-IMOGEKB4.mjs → chunk-2DKQKZML.mjs} +188 -106
  9. package/dist/{chunk-7JR73TOH.mjs → chunk-2RGJTPYF.mjs} +36 -25
  10. package/dist/{chunk-ASTSSSL2.mjs → chunk-EWYXSN2C.mjs} +75 -122
  11. package/dist/{chunk-F4MT6GBK.mjs → chunk-FGQ2I2YH.mjs} +1 -1
  12. package/dist/chunk-I7QBCEV7.mjs +33 -0
  13. package/dist/{chunk-R6BWRY3E.mjs → chunk-ILU426CF.mjs} +3 -1
  14. package/dist/{chunk-HB4Q7VYK.mjs → chunk-IUDD5FYL.mjs} +28 -273
  15. package/dist/{chunk-WRZFATUT.mjs → chunk-KWRIBHC3.mjs} +229 -142
  16. package/dist/{chunk-57SJ566R.mjs → chunk-LBJAJLKU.mjs} +1 -1
  17. package/dist/{chunk-BAFQ2GOA.mjs → chunk-LXGQCG56.mjs} +1 -1
  18. package/dist/{chunk-SRPGIWCF.mjs → chunk-ONKESAQA.mjs} +2 -2
  19. package/dist/chunk-QA7TP5EO.mjs +448 -0
  20. package/dist/chunk-UPZH6KCF.mjs +3306 -0
  21. package/dist/{chunk-5EU32E7X.mjs → chunk-VRGB6DIS.mjs} +116 -12
  22. package/dist/{config-ARLE6PKR.mjs → config-TWEO2R4N.mjs} +3 -3
  23. package/dist/{dev-6RG5SSZ7.mjs → dev-2OULECTU.mjs} +7 -7
  24. package/dist/dev-PJMEGTAC.mjs +42 -0
  25. package/dist/{discovery-FCVGQQVD.mjs → discovery-7FXND7Y6.mjs} +3 -3
  26. package/dist/{generators-UI2LJK3O.mjs → generators-4BP7B47W.mjs} +11 -34
  27. package/dist/generators-Z4XLSMC7.mjs +520 -0
  28. package/dist/index.mjs +167 -83
  29. package/dist/{media-migrations-JQSDCC7S.mjs → media-migrations-BFEL7NFG.mjs} +9 -20
  30. package/dist/media-migrations-VR7DLLR6.mjs +106 -0
  31. package/dist/{queue-BY3PLH4I.mjs → queue-SVOJPTRO.mjs} +10 -10
  32. package/dist/queue-YCBQTCYI.mjs +625 -0
  33. package/dist/{queue-migrations-YZUKEZK7.mjs → queue-migrations-HPXOO3NA.mjs} +13 -12
  34. package/dist/queue-migrations-X4P7FZKJ.mjs +167 -0
  35. package/dist/{runtime-BI343WHS.mjs → runtime-CPKR663Y.mjs} +9 -9
  36. package/dist/runtime-GIE56H47.mjs +57 -0
  37. package/dist/{runtime-ZKD6URAV.mjs → runtime-GSXF4NB3.mjs} +1 -1
  38. package/dist/runtime-worker.d.ts +2 -0
  39. package/dist/runtime-worker.mjs +242 -0
  40. package/dist/{scaffold-UBOS2NZR.mjs → scaffold-3QPGYQEQ.mjs} +9 -5
  41. package/dist/scaffold-RGAAHC6I.mjs +139 -0
  42. package/dist/{security-TYPVOYGF.mjs → security-7H5TNHZY.mjs} +6 -6
  43. package/dist/security-BZGD6ONY.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() },
@@ -1263,6 +1251,9 @@ function renderFrameworkRunner(options) {
1263
1251
  " while (true) {",
1264
1252
  " const stderrLines = []",
1265
1253
  " const childEnv = { ...process.env }",
1254
+ " if (mode === 'build') {",
1255
+ " childEnv.HOLO_INTERNAL_FRAMEWORK_BUILD = '1'",
1256
+ " }",
1266
1257
  " const preloads = [runtimeSchemaPath]",
1267
1258
  " if (framework === 'next') {",
1268
1259
  " preloads.push(nextRuntimeBootstrapPath)",
@@ -1335,13 +1326,13 @@ function renderFrameworkRunner(options) {
1335
1326
  // src/project/registry.ts
1336
1327
  import { constants as fsConstants } from "fs";
1337
1328
  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";
1329
+ import { dirname as dirname3, extname as extname2, join, resolve as resolve3 } from "path";
1339
1330
  import { loadConfigDirectory } from "@holo-js/config";
1340
1331
  import { DEFAULT_HOLO_PROJECT_PATHS, renderGeneratedSchemaRuntimeModule } from "@holo-js/db";
1341
1332
 
1342
1333
  // 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";
1334
+ import { mkdir, readdir, readFile, rm, stat, writeFile } from "fs/promises";
1335
+ import { dirname as dirname2, extname, relative as relative2, resolve as resolve2, sep } from "path";
1345
1336
  function renderManagedSvelteHooksModule() {
1346
1337
  return [
1347
1338
  "// Generated by holo prepare. Do not edit.",
@@ -1395,7 +1386,10 @@ function renderManagedSvelteServerHooksModule(features) {
1395
1386
  "import { adapterSvelteKitInternals, runWithSvelteKitRequestEvent } from '@holo-js/adapter-sveltekit'",
1396
1387
  "import { sequence } from '@sveltejs/kit/hooks'",
1397
1388
  ...features.authEnabled ? ["import auth, { check, isAuthError, provider, user } from '@holo-js/auth'"] : [],
1398
- ...features.broadcastEnabled ? ["import { renderBroadcastAuthResponse } from '@holo-js/broadcast/auth'"] : [],
1389
+ ...features.broadcastEnabled ? [
1390
+ "import { renderBroadcastAuthResponse } from '@holo-js/broadcast/auth'",
1391
+ "import { renderBroadcastClientConfigResponse } from '@holo-js/broadcast/client-config'"
1392
+ ] : [],
1399
1393
  ...features.storageEnabled ? ["import { createPublicStorageResponse } from '@holo-js/storage'"] : [],
1400
1394
  ...features.clerkEnabled ? ["import { completeClerkAuth, loginWithClerk, logoutWithClerk, registerWithClerk } from '@holo-js/auth-clerk'"] : [],
1401
1395
  ...features.workosEnabled ? ["import { completeWorkosAuth, loginWithWorkos, logoutWithWorkos, registerWithWorkos } from '@holo-js/auth-workos'"] : [],
@@ -1406,7 +1400,7 @@ function renderManagedSvelteServerHooksModule(features) {
1406
1400
  const routeHandlers = [
1407
1401
  ...features.authEnabled ? ["handleHoloCurrentAuthRoute"] : [],
1408
1402
  ...features.storageEnabled ? ["handleHoloStorageRoute"] : [],
1409
- ...features.broadcastEnabled ? ["handleHoloBroadcastAuthRoute"] : [],
1403
+ ...features.broadcastEnabled ? ["handleHoloBroadcastConfigRoute", "handleHoloBroadcastAuthRoute"] : [],
1410
1404
  ...features.clerkEnabled ? [
1411
1405
  "handleHoloClerkLoginRoute",
1412
1406
  "handleHoloClerkRegisterRoute",
@@ -1499,14 +1493,33 @@ function renderManagedSvelteServerHooksModule(features) {
1499
1493
  ""
1500
1494
  ] : [],
1501
1495
  ...features.broadcastEnabled ? [
1496
+ "async function handleHoloBroadcastConfigRoute(event: RequestEvent, app: HoloApp): Promise<Response | undefined> {",
1497
+ " if (!isRoute(event, 'GET', ['/broadcasting/config'])) {",
1498
+ " return undefined",
1499
+ " }",
1500
+ "",
1501
+ " return renderBroadcastClientConfigResponse(app.config.broadcast)",
1502
+ "}",
1503
+ "",
1502
1504
  "async function handleHoloBroadcastAuthRoute(event: RequestEvent, app: HoloApp): Promise<Response | undefined> {",
1503
1505
  " if (!isRoute(event, 'POST', ['/broadcasting/auth'])) {",
1504
1506
  " return undefined",
1505
1507
  " }",
1506
1508
  "",
1507
1509
  " const auth = await holo.getAuth()",
1510
+ " const connection = app.config.broadcast.connections[app.config.broadcast.default]",
1511
+ " const signing = connection?.driver === 'holo' && 'key' in connection && 'secret' in connection",
1512
+ " ? {",
1513
+ " appKey: connection.key,",
1514
+ " appSecret: connection.secret,",
1515
+ " }",
1516
+ " : {}",
1508
1517
  " return await renderBroadcastAuthResponse(event.request, {",
1509
- " resolveUser: async () => await auth?.user(),",
1518
+ " ...signing,",
1519
+ " resolveUser: async (_request, context) => {",
1520
+ " const guardAuth = context.guard ? auth?.guard(context.guard) : undefined",
1521
+ " return guardAuth ? await guardAuth.user() : await auth?.user()",
1522
+ " },",
1510
1523
  " channelAuth: {",
1511
1524
  " registry: {",
1512
1525
  " projectRoot: app.projectRoot,",
@@ -1634,23 +1647,7 @@ function renderSvelteHostedAuthHookRoutes(providerName, functionSuffix) {
1634
1647
  " return undefined",
1635
1648
  " }",
1636
1649
  "",
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)",
1650
+ ` return await logoutWith${functionSuffix}(event)`,
1654
1651
  "}",
1655
1652
  ""
1656
1653
  ];
@@ -1723,6 +1720,9 @@ async function unlinkIfPresent(path) {
1723
1720
  async function pathExists(path) {
1724
1721
  return await readFileIfPresent(path) !== void 0;
1725
1722
  }
1723
+ async function directoryExists(path) {
1724
+ return await stat(path).then((entry) => entry.isDirectory(), () => false);
1725
+ }
1726
1726
  var SVELTE_HOOKS_OVERRIDE_BLOCK = [
1727
1727
  " files: {",
1728
1728
  " hooks: {",
@@ -1883,17 +1883,43 @@ async function directoryContainsText(path, pattern) {
1883
1883
  }
1884
1884
  return false;
1885
1885
  }
1886
+ var realtimeDefinitionExtensions = /* @__PURE__ */ new Set([".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"]);
1887
+ async function collectRealtimeDefinitionFiles(root) {
1888
+ const entries = await readdir(root, { withFileTypes: true }).catch(() => []);
1889
+ const files = await Promise.all(entries.map(async (entry) => {
1890
+ const entryPath = resolve2(root, entry.name);
1891
+ if (entry.isDirectory()) {
1892
+ return await collectRealtimeDefinitionFiles(entryPath);
1893
+ }
1894
+ return realtimeDefinitionExtensions.has(extname(entry.name)) ? [entryPath] : [];
1895
+ }));
1896
+ return files.flat().sort((left, right) => left.localeCompare(right));
1897
+ }
1898
+ async function renderNextRealtimeDefinitions(projectRoot) {
1899
+ const generatedRoot = resolve2(projectRoot, ".holo-js/generated/next");
1900
+ const files = await collectRealtimeDefinitionFiles(resolve2(projectRoot, "server/realtime"));
1901
+ const importPaths = files.map((filePath) => {
1902
+ const withoutExtension = filePath.slice(0, -extname(filePath).length);
1903
+ const importPath = relative2(generatedRoot, withoutExtension).split(sep).join("/");
1904
+ return importPath.startsWith(".") ? importPath : `./${importPath}`;
1905
+ });
1906
+ return renderNextGeneratedRealtimeDefinitions(importPaths);
1907
+ }
1886
1908
  async function ensureNextManagedRoutes(projectRoot) {
1887
1909
  const authEnabled = await pathExists(resolve2(projectRoot, "app/api/auth/user/route.ts"));
1888
1910
  const storageEnabled = await pathExists(resolve2(projectRoot, "app/storage/[[...path]]/route.ts"));
1911
+ const broadcastEnabled = await pathExists(resolve2(projectRoot, "app/broadcasting/config/route.ts"));
1889
1912
  const broadcastAuthEnabled = await pathExists(resolve2(projectRoot, "app/broadcasting/auth/route.ts"));
1913
+ const realtimeEnabled = await directoryExists(resolve2(projectRoot, "server/realtime"));
1890
1914
  const clerkEnabled = await pathExists(resolve2(projectRoot, "app/api/auth/clerk/login/route.ts"));
1891
1915
  const workosEnabled = await pathExists(resolve2(projectRoot, "app/api/auth/workos/login/route.ts"));
1892
1916
  const files = [
1893
1917
  ...renderNextManagedRouteFiles({
1894
1918
  authEnabled,
1919
+ broadcastEnabled,
1895
1920
  storageEnabled,
1896
- broadcastAuthEnabled
1921
+ broadcastAuthEnabled,
1922
+ realtimeEnabled
1897
1923
  }),
1898
1924
  ...renderNextManagedHostedAuthRouteFiles({
1899
1925
  clerk: clerkEnabled,
@@ -1903,6 +1929,12 @@ async function ensureNextManagedRoutes(projectRoot) {
1903
1929
  for (const file of files) {
1904
1930
  await writeFileIfChanged(resolve2(projectRoot, file.path), file.contents);
1905
1931
  }
1932
+ if (realtimeEnabled) {
1933
+ await writeFileIfChanged(
1934
+ resolve2(projectRoot, ".holo-js/generated/next/realtime-definitions.ts"),
1935
+ await renderNextRealtimeDefinitions(projectRoot)
1936
+ );
1937
+ }
1906
1938
  await removeLegacyManagedHoloHelper(resolve2(projectRoot, "server/holo.ts"), renderNextHoloHelper());
1907
1939
  }
1908
1940
  function isRecord2(value) {
@@ -2137,8 +2169,8 @@ function renderAugmentationInterface(name, members) {
2137
2169
  ];
2138
2170
  }
2139
2171
  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)));
2172
+ const typedBroadcastEntries = broadcast.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2173
+ const typedChannelEntries = channels.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2142
2174
  const broadcastImportNameByName = new Map(typedBroadcastEntries.map((entry, index) => [entry.name, `holoBroadcastModule${index}`]));
2143
2175
  const channelImportNameByPattern = new Map(typedChannelEntries.map((entry, index) => [entry.pattern, `holoChannelModule${index}`]));
2144
2176
  const broadcastNeedsDefinitionType = broadcast.some((entry) => !broadcastImportNameByName.get(entry.name) || !entry.exportName);
@@ -2220,6 +2252,7 @@ function renderGeneratedBroadcastManifest(registry) {
2220
2252
  ` name: ${JSON.stringify(entry.pattern)},`,
2221
2253
  ` pattern: ${JSON.stringify(entry.pattern)},`,
2222
2254
  ` type: ${JSON.stringify(entry.type)},`,
2255
+ ...typeof entry.guard === "undefined" ? [] : [` guard: ${JSON.stringify(entry.guard)},`],
2223
2256
  ` params: ${JSON.stringify(entry.params)},`,
2224
2257
  ` whispers: ${JSON.stringify(entry.whispers)},`
2225
2258
  ];
@@ -2245,6 +2278,7 @@ function renderGeneratedBroadcastManifest(registry) {
2245
2278
  "type GeneratedBroadcastManifestChannel = {",
2246
2279
  " readonly name: string",
2247
2280
  " readonly pattern: string",
2281
+ " readonly guard?: string",
2248
2282
  " readonly type: 'private' | 'presence'",
2249
2283
  " readonly params: readonly string[]",
2250
2284
  " readonly whispers: readonly string[]",
@@ -2273,9 +2307,33 @@ function renderGeneratedBroadcastManifest(registry) {
2273
2307
  ""
2274
2308
  ].join("\n");
2275
2309
  }
2310
+ function renderGeneratedChannelImporter(channels) {
2311
+ const importableChannels = channels.filter((entry) => [".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"].includes(extname2(entry.sourcePath)));
2312
+ const imports = importableChannels.map((entry, index) => {
2313
+ return `import * as holoChannelModule${index} from '${relativeImportPath(GENERATED_CHANNEL_IMPORTER_PATH, entry.sourcePath)}'`;
2314
+ });
2315
+ const cases = importableChannels.flatMap((entry, index) => [
2316
+ ` if (normalizedPath.endsWith(${JSON.stringify(`/${entry.sourcePath}`)})) {`,
2317
+ ` return holoChannelModule${index}`,
2318
+ " }"
2319
+ ]);
2320
+ return [
2321
+ "// Generated by holo prepare. Do not edit.",
2322
+ "",
2323
+ ...imports,
2324
+ ...imports.length > 0 ? [""] : [],
2325
+ "export async function importBroadcastChannelModule(absolutePath: string): Promise<unknown> {",
2326
+ " const normalizedPath = absolutePath.replaceAll('\\\\', '/')",
2327
+ ...cases,
2328
+ "",
2329
+ ' throw new Error(`[@holo-js/broadcast] Broadcast channel module "${absolutePath}" was not generated for this app.`)',
2330
+ "}",
2331
+ ""
2332
+ ].join("\n");
2333
+ }
2276
2334
  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)));
2335
+ const typedEventEntries = events.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2336
+ const typedListenerEntries = listeners.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2279
2337
  const eventImportNameByName = new Map(typedEventEntries.map((entry, index) => [entry.name, `holoEventModule${index}`]));
2280
2338
  const listenerImportNameById = new Map(typedListenerEntries.map((entry, index) => [entry.id, `holoListenerModule${index}`]));
2281
2339
  const needsEventDefinitionType = events.some((entry) => !eventImportNameByName.get(entry.name) || !entry.exportName);
@@ -2328,7 +2386,7 @@ function renderGeneratedEventTypes(events, listeners) {
2328
2386
  ].join("\n");
2329
2387
  }
2330
2388
  function renderGeneratedQueueTypes(jobs) {
2331
- const typedJobs = jobs.filter((entry) => [".ts", ".mts", ".cts"].includes(extname(entry.sourcePath)));
2389
+ const typedJobs = jobs.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2332
2390
  const typeImportNameByJob = new Map(
2333
2391
  typedJobs.map((entry, index) => {
2334
2392
  return [entry.name, `holoQueueJobModule${index}`];
@@ -2364,7 +2422,23 @@ function renderGeneratedQueueTypes(jobs) {
2364
2422
  ""
2365
2423
  ].join("\n");
2366
2424
  }
2367
- function renderGeneratedAuthTypes(guards) {
2425
+ function getAuthProviderModelNames(auth) {
2426
+ if (!auth) {
2427
+ return [];
2428
+ }
2429
+ 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));
2430
+ }
2431
+ function renderGeneratedAuthTypes(auth, models = []) {
2432
+ const guards = auth?.guards ?? {};
2433
+ const modelByName = new Map(models.map((model) => [model.name, model]));
2434
+ const userModels = getAuthProviderModelNames(auth).map((modelName) => modelByName.get(modelName)).filter((model) => Boolean(model));
2435
+ const userTableNames = [...new Set(userModels.map((model) => model.tableName))];
2436
+ const userType = userModels.length > 0 ? userTableNames.map((tableName) => `InferSelect<GeneratedSchemaTable<${JSON.stringify(tableName)}>>`).join(" | ") : void 0;
2437
+ const imports = userType ? [
2438
+ "import type { AuthUserLike } from '@holo-js/auth'",
2439
+ "import type { GeneratedSchemaTable, InferSelect } from '@holo-js/db'",
2440
+ ""
2441
+ ] : [];
2368
2442
  const members = Object.entries(guards).sort(([left], [right]) => left.localeCompare(right)).filter((entry) => {
2369
2443
  return entry[1].driver === "session" || entry[1].driver === "token";
2370
2444
  }).map(([name, guard]) => {
@@ -2381,12 +2455,14 @@ function renderGeneratedAuthTypes(guards) {
2381
2455
  "// Generated by holo prepare. Do not edit.",
2382
2456
  "",
2383
2457
  "import '@holo-js/auth'",
2458
+ ...imports,
2384
2459
  "",
2385
2460
  "declare global {",
2386
2461
  " // eslint-disable-next-line @typescript-eslint/no-namespace",
2387
2462
  " namespace HoloAuth {",
2388
2463
  " interface TypeRegistry {",
2389
2464
  ...guardsMember,
2465
+ ...userType ? [` user: (${userType}) & AuthUserLike`] : [],
2390
2466
  " }",
2391
2467
  " }",
2392
2468
  "}",
@@ -2433,8 +2509,8 @@ function renderGeneratedAuthorizationRegistry(registry) {
2433
2509
  ].join("\n");
2434
2510
  }
2435
2511
  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)));
2512
+ const typedPolicyEntries = authorizationPolicies.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2513
+ const typedAbilityEntries = authorizationAbilities.filter((entry) => [".ts", ".mts", ".cts"].includes(extname2(entry.sourcePath)));
2438
2514
  const policyImportNameByName = new Map(typedPolicyEntries.map((entry, index) => [entry.name, `holoAuthorizationPolicyModule${index}`]));
2439
2515
  const abilityImportNameByName = new Map(typedAbilityEntries.map((entry, index) => [entry.name, `holoAuthorizationAbilityModule${index}`]));
2440
2516
  const usesHoloAuthUser = authorizationPolicies.length > 0 || authorizationAbilities.length > 0 || guardNames.length > 0;
@@ -2526,7 +2602,7 @@ async function resolveGeneratedTsconfigBase(_projectRoot) {
2526
2602
  return "../../tsconfig.json";
2527
2603
  }
2528
2604
  function getConfigExtensionPriority(fileName) {
2529
- const extension = extname(fileName);
2605
+ const extension = extname2(fileName);
2530
2606
  const index = CONFIG_EXTENSION_PRIORITY.indexOf(extension);
2531
2607
  return index >= 0 ? index : Number.MAX_SAFE_INTEGER;
2532
2608
  }
@@ -2538,7 +2614,7 @@ async function collectProjectConfigEntries(projectRoot) {
2538
2614
  if (!entry.isFile()) {
2539
2615
  continue;
2540
2616
  }
2541
- const extension = extname(entry.name);
2617
+ const extension = extname2(entry.name);
2542
2618
  if (!SUPPORTED_CONFIG_EXTENSIONS.has(extension)) {
2543
2619
  continue;
2544
2620
  }
@@ -2635,9 +2711,10 @@ async function writeGeneratedProjectRegistry(projectRoot, registry) {
2635
2711
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_LISTENERS_PATH), renderGeneratedModule("listeners", nextRegistry.listeners));
2636
2712
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_BROADCAST_PATH), renderGeneratedModule("broadcast", nextRegistry.broadcast));
2637
2713
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_CHANNELS_PATH), renderGeneratedModule("channels", nextRegistry.channels));
2714
+ await writeFileIfChanged2(resolve3(projectRoot, GENERATED_CHANNEL_IMPORTER_PATH), renderGeneratedChannelImporter(nextRegistry.channels));
2638
2715
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_BROADCAST_MANIFEST_PATH), renderGeneratedBroadcastManifest(nextRegistry));
2639
2716
  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 ?? {}));
2717
+ await writeFileIfChanged2(resolve3(projectRoot, GENERATED_AUTH_TYPES_PATH), renderGeneratedAuthTypes(loadedConfig?.auth, nextRegistry.models));
2641
2718
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_CONFIG_TYPES_PATH), renderGeneratedConfigTypes(projectRoot, configEntries));
2642
2719
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_EVENT_TYPES_PATH), renderGeneratedEventTypes(nextRegistry.events, nextRegistry.listeners));
2643
2720
  await writeFileIfChanged2(resolve3(projectRoot, GENERATED_QUEUE_TYPES_PATH), renderGeneratedQueueTypes(nextRegistry.jobs));
@@ -2719,9 +2796,14 @@ export {
2719
2796
  resolveNameInfo,
2720
2797
  resolveArtifactPath,
2721
2798
  renderNextHoloHelper,
2799
+ renderNextRuntimeBootstrap,
2722
2800
  renderNextBroadcastAuthRoute,
2801
+ renderNextBroadcastConfigRoute,
2802
+ renderNextGeneratedBroadcastConfigRoute,
2723
2803
  renderNextGeneratedBroadcastAuthRoute,
2804
+ renderNextGeneratedRealtimeDefinitions,
2724
2805
  renderNextManagedHostedAuthRouteFiles,
2806
+ renderSvelteViteConfig,
2725
2807
  renderSvelteHoloHelper,
2726
2808
  renderAuthProviderRouteFiles,
2727
2809
  renderAuthRouteFiles,