@holo-js/cli 0.1.5 → 0.1.7
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 +45 -31
- package/dist/{broadcast-RT5KVZWP.mjs → broadcast-2SYWLGDX.mjs} +4 -4
- package/dist/{cache-NHCCHT44.mjs → cache-FEKU2TK4.mjs} +4 -4
- package/dist/{cache-migrations-R2RL2RVD.mjs → cache-migrations-7RLM2QIS.mjs} +5 -5
- package/dist/{chunk-UZTDQKIY.mjs → chunk-2SE5STJ2.mjs} +1 -1
- package/dist/{chunk-OZUDZEAW.mjs → chunk-3KLRV6JF.mjs} +23 -5
- package/dist/{chunk-SCCPDJGO.mjs → chunk-42ASVV4I.mjs} +6 -4
- package/dist/{chunk-MXKNQACM.mjs → chunk-GEMG3HIO.mjs} +1183 -1
- package/dist/{chunk-VCEO6N5T.mjs → chunk-GWW5VBKY.mjs} +86 -1142
- package/dist/{chunk-5BLEC66P.mjs → chunk-PWTS5LZZ.mjs} +1 -1
- package/dist/{config-5JSC6KJG.mjs → config-IZR77Y5M.mjs} +2 -2
- package/dist/{dev-OSLYSBTL.mjs → dev-KBHPQSHI.mjs} +5 -5
- package/dist/{discovery-JLT2EOGH.mjs → discovery-BKA2Z6PR.mjs} +2 -2
- package/dist/{generators-ZIWACCBE.mjs → generators-K7A7MNY6.mjs} +5 -5
- package/dist/index.mjs +45 -31
- package/dist/{media-migrations-UBAL2YVV.mjs → media-migrations-RBF4QZZ5.mjs} +6 -6
- package/dist/{queue-I66EISVS.mjs → queue-PSPL63KS.mjs} +6 -6
- package/dist/{queue-migrations-UIAMAB6E.mjs → queue-migrations-3PI7RAB3.mjs} +5 -5
- package/dist/{runtime-MMQGO4PP.mjs → runtime-PGE4HSSF.mjs} +5 -5
- package/dist/{scaffold-ISDVICNQ.mjs → scaffold-AR5QZYMK.mjs} +7 -6
- package/dist/{security-OZXTMYXF.mjs → security-JEV55QRY.mjs} +4 -4
- package/package.json +7 -7
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadProjectConfig,
|
|
3
3
|
resolveGeneratedSchemaPath
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PWTS5LZZ.mjs";
|
|
5
5
|
import {
|
|
6
6
|
loadGeneratedProjectRegistry,
|
|
7
7
|
relativeImportPath,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
renderAuthProviderRouteFiles,
|
|
9
|
+
renderAuthRouteFiles,
|
|
10
|
+
renderFrameworkFiles,
|
|
11
|
+
renderFrameworkRunner,
|
|
12
|
+
renderGeneratedModelTypes,
|
|
13
|
+
renderNextBroadcastAuthRoute,
|
|
14
|
+
renderNextGeneratedBroadcastAuthRoute,
|
|
15
|
+
renderNextHoloHelper,
|
|
16
|
+
renderNextManagedHostedAuthRouteFiles,
|
|
17
|
+
renderSvelteHoloHelper
|
|
18
|
+
} from "./chunk-GEMG3HIO.mjs";
|
|
10
19
|
import {
|
|
11
20
|
AUTH_CONFIG_FILE_NAMES,
|
|
12
21
|
AUTH_SOCIAL_PROVIDER_PACKAGE_NAMES,
|
|
@@ -50,7 +59,7 @@ import { loadConfigDirectory } from "@holo-js/config";
|
|
|
50
59
|
// package.json
|
|
51
60
|
var package_default = {
|
|
52
61
|
name: "@holo-js/cli",
|
|
53
|
-
version: "0.1.
|
|
62
|
+
version: "0.1.7",
|
|
54
63
|
description: "Holo-JS Framework - project creation, discovery, and operational CLI",
|
|
55
64
|
type: "module",
|
|
56
65
|
license: "MIT",
|
|
@@ -101,48 +110,48 @@ var package_default = {
|
|
|
101
110
|
var WORKSPACE_CATALOG = Object.freeze({
|
|
102
111
|
"@clerk/backend": "^3.4.7",
|
|
103
112
|
"@eslint/js": "^9.17.0",
|
|
104
|
-
"@holo-js/adapter-next": "^0.1.
|
|
105
|
-
"@holo-js/adapter-nuxt": "^0.1.
|
|
106
|
-
"@holo-js/adapter-sveltekit": "^0.1.
|
|
107
|
-
"@holo-js/auth": "^0.1.
|
|
108
|
-
"@holo-js/auth-clerk": "^0.1.
|
|
109
|
-
"@holo-js/auth-social": "^0.1.
|
|
110
|
-
"@holo-js/auth-social-apple": "^0.1.
|
|
111
|
-
"@holo-js/auth-social-discord": "^0.1.
|
|
112
|
-
"@holo-js/auth-social-facebook": "^0.1.
|
|
113
|
-
"@holo-js/auth-social-github": "^0.1.
|
|
114
|
-
"@holo-js/auth-social-google": "^0.1.
|
|
115
|
-
"@holo-js/auth-social-linkedin": "^0.1.
|
|
116
|
-
"@holo-js/auth-workos": "^0.1.
|
|
117
|
-
"@holo-js/authorization": "^0.1.
|
|
118
|
-
"@holo-js/broadcast": "^0.1.
|
|
119
|
-
"@holo-js/cache": "^0.1.
|
|
120
|
-
"@holo-js/cache-db": "^0.1.
|
|
121
|
-
"@holo-js/cache-redis": "^0.1.
|
|
122
|
-
"@holo-js/cli": "^0.1.
|
|
123
|
-
"@holo-js/config": "^0.1.
|
|
124
|
-
"@holo-js/core": "^0.1.
|
|
125
|
-
"@holo-js/db": "^0.1.
|
|
126
|
-
"@holo-js/db-mysql": "^0.1.
|
|
127
|
-
"@holo-js/db-postgres": "^0.1.
|
|
128
|
-
"@holo-js/db-sqlite": "^0.1.
|
|
129
|
-
"@holo-js/events": "^0.1.
|
|
130
|
-
"@holo-js/flux": "^0.1.
|
|
131
|
-
"@holo-js/flux-react": "^0.1.
|
|
132
|
-
"@holo-js/flux-svelte": "^0.1.
|
|
133
|
-
"@holo-js/flux-vue": "^0.1.
|
|
134
|
-
"@holo-js/forms": "^0.1.
|
|
135
|
-
"@holo-js/mail": "^0.1.
|
|
136
|
-
"@holo-js/media": "^0.1.
|
|
137
|
-
"@holo-js/notifications": "^0.1.
|
|
138
|
-
"@holo-js/queue": "^0.1.
|
|
139
|
-
"@holo-js/queue-db": "^0.1.
|
|
140
|
-
"@holo-js/queue-redis": "^0.1.
|
|
141
|
-
"@holo-js/security": "^0.1.
|
|
142
|
-
"@holo-js/session": "^0.1.
|
|
143
|
-
"@holo-js/storage": "^0.1.
|
|
144
|
-
"@holo-js/storage-s3": "^0.1.
|
|
145
|
-
"@holo-js/validation": "^0.1.
|
|
113
|
+
"@holo-js/adapter-next": "^0.1.7",
|
|
114
|
+
"@holo-js/adapter-nuxt": "^0.1.7",
|
|
115
|
+
"@holo-js/adapter-sveltekit": "^0.1.7",
|
|
116
|
+
"@holo-js/auth": "^0.1.7",
|
|
117
|
+
"@holo-js/auth-clerk": "^0.1.7",
|
|
118
|
+
"@holo-js/auth-social": "^0.1.7",
|
|
119
|
+
"@holo-js/auth-social-apple": "^0.1.7",
|
|
120
|
+
"@holo-js/auth-social-discord": "^0.1.7",
|
|
121
|
+
"@holo-js/auth-social-facebook": "^0.1.7",
|
|
122
|
+
"@holo-js/auth-social-github": "^0.1.7",
|
|
123
|
+
"@holo-js/auth-social-google": "^0.1.7",
|
|
124
|
+
"@holo-js/auth-social-linkedin": "^0.1.7",
|
|
125
|
+
"@holo-js/auth-workos": "^0.1.7",
|
|
126
|
+
"@holo-js/authorization": "^0.1.7",
|
|
127
|
+
"@holo-js/broadcast": "^0.1.7",
|
|
128
|
+
"@holo-js/cache": "^0.1.7",
|
|
129
|
+
"@holo-js/cache-db": "^0.1.7",
|
|
130
|
+
"@holo-js/cache-redis": "^0.1.7",
|
|
131
|
+
"@holo-js/cli": "^0.1.7",
|
|
132
|
+
"@holo-js/config": "^0.1.7",
|
|
133
|
+
"@holo-js/core": "^0.1.7",
|
|
134
|
+
"@holo-js/db": "^0.1.7",
|
|
135
|
+
"@holo-js/db-mysql": "^0.1.7",
|
|
136
|
+
"@holo-js/db-postgres": "^0.1.7",
|
|
137
|
+
"@holo-js/db-sqlite": "^0.1.7",
|
|
138
|
+
"@holo-js/events": "^0.1.7",
|
|
139
|
+
"@holo-js/flux": "^0.1.7",
|
|
140
|
+
"@holo-js/flux-react": "^0.1.7",
|
|
141
|
+
"@holo-js/flux-svelte": "^0.1.7",
|
|
142
|
+
"@holo-js/flux-vue": "^0.1.7",
|
|
143
|
+
"@holo-js/forms": "^0.1.7",
|
|
144
|
+
"@holo-js/mail": "^0.1.7",
|
|
145
|
+
"@holo-js/media": "^0.1.7",
|
|
146
|
+
"@holo-js/notifications": "^0.1.7",
|
|
147
|
+
"@holo-js/queue": "^0.1.7",
|
|
148
|
+
"@holo-js/queue-db": "^0.1.7",
|
|
149
|
+
"@holo-js/queue-redis": "^0.1.7",
|
|
150
|
+
"@holo-js/security": "^0.1.7",
|
|
151
|
+
"@holo-js/session": "^0.1.7",
|
|
152
|
+
"@holo-js/storage": "^0.1.7",
|
|
153
|
+
"@holo-js/storage-s3": "^0.1.7",
|
|
154
|
+
"@holo-js/validation": "^0.1.7",
|
|
146
155
|
"@nuxt/kit": "^4.4.4",
|
|
147
156
|
"@nuxt/module-builder": "^1.0.2",
|
|
148
157
|
"@sveltejs/adapter-node": "^5.5.4",
|
|
@@ -159,7 +168,7 @@ var WORKSPACE_CATALOG = Object.freeze({
|
|
|
159
168
|
"@vitest/coverage-v8": "^4.1.5",
|
|
160
169
|
"better-sqlite3": "^11.7.0",
|
|
161
170
|
"bullmq": "^5.71.0",
|
|
162
|
-
"create-holo-js": "^0.1.
|
|
171
|
+
"create-holo-js": "^0.1.7",
|
|
163
172
|
"esbuild": "^0.27.4",
|
|
164
173
|
"eslint": "^9.17.0",
|
|
165
174
|
"fast-check": "^4.5.3",
|
|
@@ -1287,28 +1296,6 @@ function renderBroadcastEnvFiles() {
|
|
|
1287
1296
|
example
|
|
1288
1297
|
};
|
|
1289
1298
|
}
|
|
1290
|
-
function renderNextBroadcastAuthRoute() {
|
|
1291
|
-
return [
|
|
1292
|
-
"import { renderBroadcastAuthResponse } from '@holo-js/broadcast/auth'",
|
|
1293
|
-
"import { holo } from '@/server/holo'",
|
|
1294
|
-
"",
|
|
1295
|
-
"export async function POST(request: Request) {",
|
|
1296
|
-
" const app = await holo.getApp()",
|
|
1297
|
-
" const auth = await holo.getAuth()",
|
|
1298
|
-
"",
|
|
1299
|
-
" return await renderBroadcastAuthResponse(request, {",
|
|
1300
|
-
" resolveUser: async () => await auth?.user(),",
|
|
1301
|
-
" channelAuth: {",
|
|
1302
|
-
" registry: {",
|
|
1303
|
-
" projectRoot: app.projectRoot,",
|
|
1304
|
-
" channels: app.registry?.channels ?? [],",
|
|
1305
|
-
" },",
|
|
1306
|
-
" },",
|
|
1307
|
-
" })",
|
|
1308
|
-
"}",
|
|
1309
|
-
""
|
|
1310
|
-
].join("\n");
|
|
1311
|
-
}
|
|
1312
1299
|
function renderNuxtBroadcastAuthRoute() {
|
|
1313
1300
|
return [
|
|
1314
1301
|
"import { defineEventHandler, getHeaders, getRequestURL, readRawBody } from 'h3'",
|
|
@@ -1398,10 +1385,12 @@ async function syncBroadcastAuthSupportAfterAuthInstall(projectRoot) {
|
|
|
1398
1385
|
}
|
|
1399
1386
|
if (framework === "next") {
|
|
1400
1387
|
const authRoutePath = resolve2(projectRoot, "app/broadcasting/auth/route.ts");
|
|
1388
|
+
const generatedRoutePath = resolve2(projectRoot, ".holo-js/generated/next/broadcast-auth-route.ts");
|
|
1401
1389
|
if (!await pathExists(authRoutePath)) {
|
|
1402
1390
|
await writeTextFile(authRoutePath, renderNextBroadcastAuthRoute());
|
|
1403
1391
|
createdBroadcastAuthRoute = true;
|
|
1404
1392
|
}
|
|
1393
|
+
await writeTextFile(generatedRoutePath, renderNextGeneratedBroadcastAuthRoute());
|
|
1405
1394
|
return {
|
|
1406
1395
|
updatedBroadcastConfig,
|
|
1407
1396
|
createdBroadcastAuthRoute
|
|
@@ -1517,9 +1506,6 @@ function renderAuthConfig(features = {}, moduleFormat = "esm") {
|
|
|
1517
1506
|
" emailVerification: {",
|
|
1518
1507
|
" required: false,",
|
|
1519
1508
|
" route: '/verify-email',",
|
|
1520
|
-
" },",
|
|
1521
|
-
" personalAccessTokens: {",
|
|
1522
|
-
" defaultAbilities: [],",
|
|
1523
1509
|
" },"
|
|
1524
1510
|
];
|
|
1525
1511
|
if (socialProviders.length > 0) {
|
|
@@ -1824,7 +1810,7 @@ function renderAuthMigration(slug) {
|
|
|
1824
1810
|
" await schema.createTable('sessions', (table) => {",
|
|
1825
1811
|
" table.string('id').primaryKey()",
|
|
1826
1812
|
" table.string('store').default('database')",
|
|
1827
|
-
" table.json('data')
|
|
1813
|
+
" table.json('data')",
|
|
1828
1814
|
" table.timestamp('created_at')",
|
|
1829
1815
|
" table.timestamp('last_activity_at')",
|
|
1830
1816
|
" table.timestamp('expires_at')",
|
|
@@ -1854,8 +1840,8 @@ function renderAuthMigration(slug) {
|
|
|
1854
1840
|
" table.string('provider_user_id')",
|
|
1855
1841
|
" table.string('email').nullable()",
|
|
1856
1842
|
" table.boolean('email_verified').default(false)",
|
|
1857
|
-
" table.json('profile')
|
|
1858
|
-
" table.json('tokens')
|
|
1843
|
+
" table.json('profile')",
|
|
1844
|
+
" table.json('tokens')",
|
|
1859
1845
|
" table.timestamps()",
|
|
1860
1846
|
" table.index(['user_id'])",
|
|
1861
1847
|
" table.unique(['provider', 'provider_user_id'], 'auth_identities_provider_user_unique')",
|
|
@@ -1879,7 +1865,7 @@ function renderAuthMigration(slug) {
|
|
|
1879
1865
|
" table.string('user_id')",
|
|
1880
1866
|
" table.string('name')",
|
|
1881
1867
|
" table.string('token_hash').unique()",
|
|
1882
|
-
" table.json('abilities')
|
|
1868
|
+
" table.json('abilities')",
|
|
1883
1869
|
" table.timestamp('last_used_at').nullable()",
|
|
1884
1870
|
" table.timestamp('expires_at').nullable()",
|
|
1885
1871
|
" table.timestamps()",
|
|
@@ -1962,7 +1948,7 @@ function renderNotificationsMigration() {
|
|
|
1962
1948
|
" table.string('type').nullable()",
|
|
1963
1949
|
" table.string('notifiable_type')",
|
|
1964
1950
|
" table.string('notifiable_id')",
|
|
1965
|
-
" table.json('data')
|
|
1951
|
+
" table.json('data')",
|
|
1966
1952
|
" table.timestamp('read_at').nullable()",
|
|
1967
1953
|
" table.timestamp('created_at')",
|
|
1968
1954
|
" table.timestamp('updated_at')",
|
|
@@ -2324,7 +2310,7 @@ function renderScaffoldTsconfig(options) {
|
|
|
2324
2310
|
}, null, 2)}
|
|
2325
2311
|
`;
|
|
2326
2312
|
}
|
|
2327
|
-
const include = ["next-env.d.ts", "
|
|
2313
|
+
const include = ["next-env.d.ts", "app/**/*.ts", "app/**/*.tsx", "server/**/*.ts", "config/**/*.ts", ".holo-js/generated/**/*.ts", ".holo-js/generated/**/*.d.ts"];
|
|
2328
2314
|
return `${JSON.stringify({
|
|
2329
2315
|
compilerOptions: {
|
|
2330
2316
|
target: "ES2022",
|
|
@@ -2359,1066 +2345,6 @@ function renderVSCodeSettings(options) {
|
|
|
2359
2345
|
`;
|
|
2360
2346
|
}
|
|
2361
2347
|
|
|
2362
|
-
// src/project/scaffold/framework-renderers.ts
|
|
2363
|
-
var HOSTED_AUTH_PROVIDERS = {
|
|
2364
|
-
workos: {
|
|
2365
|
-
provider: "workos",
|
|
2366
|
-
packageName: "@holo-js/auth-workos",
|
|
2367
|
-
loginFunction: "loginWithWorkos",
|
|
2368
|
-
registerFunction: "registerWithWorkos",
|
|
2369
|
-
callbackFunction: "completeWorkosAuth",
|
|
2370
|
-
logoutFunction: "logoutWithWorkos"
|
|
2371
|
-
},
|
|
2372
|
-
clerk: {
|
|
2373
|
-
provider: "clerk",
|
|
2374
|
-
packageName: "@holo-js/auth-clerk",
|
|
2375
|
-
loginFunction: "loginWithClerk",
|
|
2376
|
-
registerFunction: "registerWithClerk",
|
|
2377
|
-
callbackFunction: "completeClerkAuth",
|
|
2378
|
-
logoutFunction: "logoutWithClerk"
|
|
2379
|
-
}
|
|
2380
|
-
};
|
|
2381
|
-
function getRequestedHostedAuthProviders(features) {
|
|
2382
|
-
return [
|
|
2383
|
-
...features.workos ? ["workos"] : [],
|
|
2384
|
-
...features.clerk ? ["clerk"] : []
|
|
2385
|
-
];
|
|
2386
|
-
}
|
|
2387
|
-
function renderNuxtAppVue(projectName) {
|
|
2388
|
-
return [
|
|
2389
|
-
"<template>",
|
|
2390
|
-
' <main class="shell">',
|
|
2391
|
-
" <h1>{{ appName }}</h1>",
|
|
2392
|
-
" <p>Nuxt renders the UI. Holo owns the backend runtime and canonical server directories.</p>",
|
|
2393
|
-
" </main>",
|
|
2394
|
-
"</template>",
|
|
2395
|
-
"",
|
|
2396
|
-
'<script setup lang="ts">',
|
|
2397
|
-
`const appName = ${JSON.stringify(projectName)}`,
|
|
2398
|
-
"</script>",
|
|
2399
|
-
"",
|
|
2400
|
-
"<style scoped>",
|
|
2401
|
-
".shell {",
|
|
2402
|
-
" min-height: 100vh;",
|
|
2403
|
-
" display: grid;",
|
|
2404
|
-
" place-content: center;",
|
|
2405
|
-
" gap: 1rem;",
|
|
2406
|
-
" padding: 3rem;",
|
|
2407
|
-
" font-family: sans-serif;",
|
|
2408
|
-
"}",
|
|
2409
|
-
"h1 {",
|
|
2410
|
-
" margin: 0;",
|
|
2411
|
-
" font-size: clamp(2.5rem, 6vw, 4rem);",
|
|
2412
|
-
"}",
|
|
2413
|
-
"p {",
|
|
2414
|
-
" margin: 0;",
|
|
2415
|
-
" max-width: 40rem;",
|
|
2416
|
-
" line-height: 1.6;",
|
|
2417
|
-
"}",
|
|
2418
|
-
"</style>",
|
|
2419
|
-
""
|
|
2420
|
-
].join("\n");
|
|
2421
|
-
}
|
|
2422
|
-
function renderNuxtConfig() {
|
|
2423
|
-
return [
|
|
2424
|
-
"export default defineNuxtConfig({",
|
|
2425
|
-
" modules: ['@holo-js/adapter-nuxt'],",
|
|
2426
|
-
" sourcemap: {",
|
|
2427
|
-
" client: false,",
|
|
2428
|
-
" server: false,",
|
|
2429
|
-
" },",
|
|
2430
|
-
" vite: {",
|
|
2431
|
-
" build: {",
|
|
2432
|
-
" rollupOptions: {",
|
|
2433
|
-
" onwarn(warning, defaultHandler) {",
|
|
2434
|
-
" if (",
|
|
2435
|
-
" warning.message.includes('nuxt:module-preload-polyfill')",
|
|
2436
|
-
" && warning.message.includes('didn\\'t generate a sourcemap')",
|
|
2437
|
-
" ) {",
|
|
2438
|
-
" return",
|
|
2439
|
-
" }",
|
|
2440
|
-
"",
|
|
2441
|
-
" defaultHandler(warning)",
|
|
2442
|
-
" },",
|
|
2443
|
-
" },",
|
|
2444
|
-
" },",
|
|
2445
|
-
" },",
|
|
2446
|
-
"})",
|
|
2447
|
-
""
|
|
2448
|
-
].join("\n");
|
|
2449
|
-
}
|
|
2450
|
-
function renderNuxtHealthRoute() {
|
|
2451
|
-
return [
|
|
2452
|
-
"export default defineEventHandler(async () => {",
|
|
2453
|
-
" const app = await holo.getApp()",
|
|
2454
|
-
"",
|
|
2455
|
-
" return {",
|
|
2456
|
-
" ok: true,",
|
|
2457
|
-
" app: app.config.app.name,",
|
|
2458
|
-
" env: app.config.app.env,",
|
|
2459
|
-
" models: app.registry?.models.length ?? 0,",
|
|
2460
|
-
" commands: app.registry?.commands.length ?? 0,",
|
|
2461
|
-
" }",
|
|
2462
|
-
"})",
|
|
2463
|
-
""
|
|
2464
|
-
].join("\n");
|
|
2465
|
-
}
|
|
2466
|
-
function renderNuxtCurrentAuthRoute() {
|
|
2467
|
-
return [
|
|
2468
|
-
"import auth, { check, isAuthError, provider, user } from '@holo-js/auth'",
|
|
2469
|
-
"import { setResponseStatus } from 'h3'",
|
|
2470
|
-
"",
|
|
2471
|
-
"export default defineEventHandler(async (event) => {",
|
|
2472
|
-
" const query = getQuery(event)",
|
|
2473
|
-
" const guard = typeof query.guard === 'string' ? query.guard : undefined",
|
|
2474
|
-
" try {",
|
|
2475
|
-
" const guardAuth = guard ? auth.guard(guard) : undefined",
|
|
2476
|
-
"",
|
|
2477
|
-
" return {",
|
|
2478
|
-
" authenticated: guardAuth ? await guardAuth.check() : await check(),",
|
|
2479
|
-
" guard: guard ?? 'web',",
|
|
2480
|
-
" provider: guardAuth ? await guardAuth.provider() : await provider(),",
|
|
2481
|
-
" user: guardAuth ? await guardAuth.user() : await user(),",
|
|
2482
|
-
" }",
|
|
2483
|
-
" } catch (error) {",
|
|
2484
|
-
" if (isAuthError(error) && error.code === 'guard_not_configured') {",
|
|
2485
|
-
" setResponseStatus(event, 400)",
|
|
2486
|
-
"",
|
|
2487
|
-
" return {",
|
|
2488
|
-
" authenticated: false,",
|
|
2489
|
-
" guard: guard ?? 'web',",
|
|
2490
|
-
" provider: null,",
|
|
2491
|
-
" user: null,",
|
|
2492
|
-
" }",
|
|
2493
|
-
" }",
|
|
2494
|
-
"",
|
|
2495
|
-
" throw error",
|
|
2496
|
-
" }",
|
|
2497
|
-
"})",
|
|
2498
|
-
""
|
|
2499
|
-
].join("\n");
|
|
2500
|
-
}
|
|
2501
|
-
function renderNuxtHostedAuthLoginRoute(spec) {
|
|
2502
|
-
return [
|
|
2503
|
-
`import { ${spec.loginFunction} } from '${spec.packageName}'`,
|
|
2504
|
-
"",
|
|
2505
|
-
"export default defineEventHandler(async (event) => {",
|
|
2506
|
-
` return await ${spec.loginFunction}(event)`,
|
|
2507
|
-
"})",
|
|
2508
|
-
""
|
|
2509
|
-
].join("\n");
|
|
2510
|
-
}
|
|
2511
|
-
function renderNuxtHostedAuthRegisterRoute(spec) {
|
|
2512
|
-
return [
|
|
2513
|
-
`import { ${spec.registerFunction} } from '${spec.packageName}'`,
|
|
2514
|
-
"",
|
|
2515
|
-
"export default defineEventHandler(async (event) => {",
|
|
2516
|
-
` return await ${spec.registerFunction}(event)`,
|
|
2517
|
-
"})",
|
|
2518
|
-
""
|
|
2519
|
-
].join("\n");
|
|
2520
|
-
}
|
|
2521
|
-
function renderNuxtHostedAuthCallbackRoute(spec) {
|
|
2522
|
-
return [
|
|
2523
|
-
`import { ${spec.callbackFunction} } from '${spec.packageName}'`,
|
|
2524
|
-
"import { sendRedirect } from 'h3'",
|
|
2525
|
-
"",
|
|
2526
|
-
"export default defineEventHandler(async (event) => {",
|
|
2527
|
-
` const { error } = await ${spec.callbackFunction}(event)`,
|
|
2528
|
-
" if (error) {",
|
|
2529
|
-
" return await sendRedirect(event, `/login?error=${encodeURIComponent(error.code)}`, 303)",
|
|
2530
|
-
" }",
|
|
2531
|
-
"",
|
|
2532
|
-
" return await sendRedirect(event, '/', 303)",
|
|
2533
|
-
"})",
|
|
2534
|
-
""
|
|
2535
|
-
].join("\n");
|
|
2536
|
-
}
|
|
2537
|
-
function renderNuxtHostedAuthLogoutRoute(spec) {
|
|
2538
|
-
return [
|
|
2539
|
-
"import { provider } from '@holo-js/auth'",
|
|
2540
|
-
`import { ${spec.logoutFunction} } from '${spec.packageName}'`,
|
|
2541
|
-
"import { createError, sendRedirect } from 'h3'",
|
|
2542
|
-
"",
|
|
2543
|
-
"export default defineEventHandler(async (event) => {",
|
|
2544
|
-
" let currentProvider: string | null",
|
|
2545
|
-
" try {",
|
|
2546
|
-
" currentProvider = await provider()",
|
|
2547
|
-
" } catch {",
|
|
2548
|
-
" return await sendRedirect(event, '/', 303)",
|
|
2549
|
-
" }",
|
|
2550
|
-
"",
|
|
2551
|
-
` if (currentProvider !== '${spec.provider}') {`,
|
|
2552
|
-
" return await sendRedirect(event, '/', 303)",
|
|
2553
|
-
" }",
|
|
2554
|
-
"",
|
|
2555
|
-
` const { data, error } = await ${spec.logoutFunction}(event)`,
|
|
2556
|
-
" if (error) {",
|
|
2557
|
-
" throw createError({",
|
|
2558
|
-
" statusCode: error.status,",
|
|
2559
|
-
" statusMessage: error.message,",
|
|
2560
|
-
" })",
|
|
2561
|
-
" }",
|
|
2562
|
-
"",
|
|
2563
|
-
" return await sendRedirect(event, data.url, 303)",
|
|
2564
|
-
"})",
|
|
2565
|
-
""
|
|
2566
|
-
].join("\n");
|
|
2567
|
-
}
|
|
2568
|
-
function renderNuxtHostedAuthRouteFiles(provider) {
|
|
2569
|
-
const spec = HOSTED_AUTH_PROVIDERS[provider];
|
|
2570
|
-
return [
|
|
2571
|
-
{ path: `server/api/auth/${provider}/login.get.ts`, contents: renderNuxtHostedAuthLoginRoute(spec) },
|
|
2572
|
-
{ path: `server/api/auth/${provider}/register.get.ts`, contents: renderNuxtHostedAuthRegisterRoute(spec) },
|
|
2573
|
-
{ path: `server/api/auth/${provider}/callback.get.ts`, contents: renderNuxtHostedAuthCallbackRoute(spec) },
|
|
2574
|
-
{ path: `server/api/auth/${provider}/logout.post.ts`, contents: renderNuxtHostedAuthLogoutRoute(spec) }
|
|
2575
|
-
];
|
|
2576
|
-
}
|
|
2577
|
-
function renderNextConfig() {
|
|
2578
|
-
return [
|
|
2579
|
-
"import type { NextConfig } from 'next'",
|
|
2580
|
-
"import { withHolo } from '@holo-js/adapter-next/config'",
|
|
2581
|
-
"",
|
|
2582
|
-
"const nextConfig: NextConfig = withHolo({",
|
|
2583
|
-
" /* config options here */",
|
|
2584
|
-
"})",
|
|
2585
|
-
"",
|
|
2586
|
-
"export default nextConfig",
|
|
2587
|
-
""
|
|
2588
|
-
].join("\n");
|
|
2589
|
-
}
|
|
2590
|
-
function renderNextLayout(projectName) {
|
|
2591
|
-
return [
|
|
2592
|
-
"import type { ReactNode } from 'react'",
|
|
2593
|
-
"",
|
|
2594
|
-
"export const metadata = {",
|
|
2595
|
-
` title: ${JSON.stringify(projectName)},`,
|
|
2596
|
-
" description: 'Holo on Next.js',",
|
|
2597
|
-
"}",
|
|
2598
|
-
"",
|
|
2599
|
-
"export default function RootLayout({ children }: { children: ReactNode }) {",
|
|
2600
|
-
" return (",
|
|
2601
|
-
' <html lang="en">',
|
|
2602
|
-
" <body>{children}</body>",
|
|
2603
|
-
" </html>",
|
|
2604
|
-
" )",
|
|
2605
|
-
"}",
|
|
2606
|
-
""
|
|
2607
|
-
].join("\n");
|
|
2608
|
-
}
|
|
2609
|
-
function escapeHtml(value) {
|
|
2610
|
-
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'").replaceAll("{", "{").replaceAll("}", "}");
|
|
2611
|
-
}
|
|
2612
|
-
function renderNextPage(projectName) {
|
|
2613
|
-
const escapedProjectName = escapeHtml(projectName);
|
|
2614
|
-
return [
|
|
2615
|
-
"export default function HomePage() {",
|
|
2616
|
-
" return (",
|
|
2617
|
-
" <main style={{ padding: '3rem', fontFamily: 'sans-serif' }}>",
|
|
2618
|
-
` <h1>${escapedProjectName}</h1>`,
|
|
2619
|
-
" <p>Next.js handles rendering. Holo powers the backend runtime and discovered server resources.</p>",
|
|
2620
|
-
" </main>",
|
|
2621
|
-
" )",
|
|
2622
|
-
"}",
|
|
2623
|
-
""
|
|
2624
|
-
].join("\n");
|
|
2625
|
-
}
|
|
2626
|
-
function renderNextEnvDts() {
|
|
2627
|
-
return [
|
|
2628
|
-
'/// <reference types="next" />',
|
|
2629
|
-
'/// <reference types="next/image-types/global" />',
|
|
2630
|
-
"",
|
|
2631
|
-
"// Generated by Holo. Do not edit.",
|
|
2632
|
-
""
|
|
2633
|
-
].join("\n");
|
|
2634
|
-
}
|
|
2635
|
-
function renderNextHoloHelper() {
|
|
2636
|
-
return [
|
|
2637
|
-
"import { createNextHoloHelpers } from '@holo-js/adapter-next'",
|
|
2638
|
-
"",
|
|
2639
|
-
"export const holo = createNextHoloHelpers()",
|
|
2640
|
-
""
|
|
2641
|
-
].join("\n");
|
|
2642
|
-
}
|
|
2643
|
-
function renderNextInstrumentation() {
|
|
2644
|
-
return [
|
|
2645
|
-
"export async function register() {",
|
|
2646
|
-
" if (process.env.NEXT_RUNTIME === 'nodejs') {",
|
|
2647
|
-
" const { holo } = await import('@/server/holo')",
|
|
2648
|
-
" await holo.getApp()",
|
|
2649
|
-
" }",
|
|
2650
|
-
"}",
|
|
2651
|
-
""
|
|
2652
|
-
].join("\n");
|
|
2653
|
-
}
|
|
2654
|
-
function renderNextHealthRoute() {
|
|
2655
|
-
return [
|
|
2656
|
-
"import { holo } from '@/server/holo'",
|
|
2657
|
-
"",
|
|
2658
|
-
"export async function GET() {",
|
|
2659
|
-
" const app = await holo.getApp()",
|
|
2660
|
-
"",
|
|
2661
|
-
" return Response.json({",
|
|
2662
|
-
" ok: true,",
|
|
2663
|
-
" app: app.config.app.name,",
|
|
2664
|
-
" env: app.config.app.env,",
|
|
2665
|
-
" models: app.registry?.models.length ?? 0,",
|
|
2666
|
-
" commands: app.registry?.commands.length ?? 0,",
|
|
2667
|
-
" })",
|
|
2668
|
-
"}",
|
|
2669
|
-
""
|
|
2670
|
-
].join("\n");
|
|
2671
|
-
}
|
|
2672
|
-
function renderNextCurrentAuthRoute() {
|
|
2673
|
-
return [
|
|
2674
|
-
"import auth, { check, isAuthError, provider, user } from '@holo-js/auth'",
|
|
2675
|
-
"",
|
|
2676
|
-
"export async function GET(request: Request) {",
|
|
2677
|
-
" const guard = new URL(request.url).searchParams.get('guard') ?? undefined",
|
|
2678
|
-
" try {",
|
|
2679
|
-
" const guardAuth = guard ? auth.guard(guard) : undefined",
|
|
2680
|
-
"",
|
|
2681
|
-
" return Response.json({",
|
|
2682
|
-
" authenticated: guardAuth ? await guardAuth.check() : await check(),",
|
|
2683
|
-
" guard: guard ?? 'web',",
|
|
2684
|
-
" provider: guardAuth ? await guardAuth.provider() : await provider(),",
|
|
2685
|
-
" user: guardAuth ? await guardAuth.user() : await user(),",
|
|
2686
|
-
" })",
|
|
2687
|
-
" } catch (error) {",
|
|
2688
|
-
" if (isAuthError(error) && error.code === 'guard_not_configured') {",
|
|
2689
|
-
" return Response.json({",
|
|
2690
|
-
" authenticated: false,",
|
|
2691
|
-
" guard: guard ?? 'web',",
|
|
2692
|
-
" provider: null,",
|
|
2693
|
-
" user: null,",
|
|
2694
|
-
" }, { status: 400 })",
|
|
2695
|
-
" }",
|
|
2696
|
-
"",
|
|
2697
|
-
" throw error",
|
|
2698
|
-
" }",
|
|
2699
|
-
"}",
|
|
2700
|
-
""
|
|
2701
|
-
].join("\n");
|
|
2702
|
-
}
|
|
2703
|
-
function renderNextHostedAuthLoginRoute(spec) {
|
|
2704
|
-
return [
|
|
2705
|
-
`import { ${spec.loginFunction} } from '${spec.packageName}'`,
|
|
2706
|
-
"",
|
|
2707
|
-
"export async function GET(request: Request) {",
|
|
2708
|
-
` return await ${spec.loginFunction}(request)`,
|
|
2709
|
-
"}",
|
|
2710
|
-
""
|
|
2711
|
-
].join("\n");
|
|
2712
|
-
}
|
|
2713
|
-
function renderNextHostedAuthRegisterRoute(spec) {
|
|
2714
|
-
return [
|
|
2715
|
-
`import { ${spec.registerFunction} } from '${spec.packageName}'`,
|
|
2716
|
-
"",
|
|
2717
|
-
"export async function GET(request: Request) {",
|
|
2718
|
-
` return await ${spec.registerFunction}(request)`,
|
|
2719
|
-
"}",
|
|
2720
|
-
""
|
|
2721
|
-
].join("\n");
|
|
2722
|
-
}
|
|
2723
|
-
function renderNextHostedAuthCallbackRoute(spec) {
|
|
2724
|
-
return [
|
|
2725
|
-
`import { ${spec.callbackFunction} } from '${spec.packageName}'`,
|
|
2726
|
-
"",
|
|
2727
|
-
"export async function GET(request: Request) {",
|
|
2728
|
-
` const { error } = await ${spec.callbackFunction}(request)`,
|
|
2729
|
-
" if (error) {",
|
|
2730
|
-
" return Response.redirect(new URL(`/login?error=${encodeURIComponent(error.code)}`, request.url))",
|
|
2731
|
-
" }",
|
|
2732
|
-
"",
|
|
2733
|
-
" return Response.redirect(new URL('/', request.url))",
|
|
2734
|
-
"}",
|
|
2735
|
-
""
|
|
2736
|
-
].join("\n");
|
|
2737
|
-
}
|
|
2738
|
-
function renderNextHostedAuthLogoutRoute(spec) {
|
|
2739
|
-
return [
|
|
2740
|
-
"import { provider } from '@holo-js/auth'",
|
|
2741
|
-
`import { ${spec.logoutFunction} } from '${spec.packageName}'`,
|
|
2742
|
-
"",
|
|
2743
|
-
"export async function POST(request: Request) {",
|
|
2744
|
-
" let currentProvider: string | null",
|
|
2745
|
-
" try {",
|
|
2746
|
-
" currentProvider = await provider()",
|
|
2747
|
-
" } catch {",
|
|
2748
|
-
" return Response.redirect(new URL('/', request.url), 303)",
|
|
2749
|
-
" }",
|
|
2750
|
-
"",
|
|
2751
|
-
` if (currentProvider !== '${spec.provider}') {`,
|
|
2752
|
-
" return Response.redirect(new URL('/', request.url), 303)",
|
|
2753
|
-
" }",
|
|
2754
|
-
"",
|
|
2755
|
-
` const { data, error } = await ${spec.logoutFunction}(request)`,
|
|
2756
|
-
" if (error) {",
|
|
2757
|
-
" return Response.json({ data, error }, { status: error.status })",
|
|
2758
|
-
" }",
|
|
2759
|
-
"",
|
|
2760
|
-
" return Response.redirect(data.url, 303)",
|
|
2761
|
-
"}",
|
|
2762
|
-
""
|
|
2763
|
-
].join("\n");
|
|
2764
|
-
}
|
|
2765
|
-
function renderNextHostedAuthRouteFiles(provider) {
|
|
2766
|
-
const spec = HOSTED_AUTH_PROVIDERS[provider];
|
|
2767
|
-
return [
|
|
2768
|
-
{ path: `app/api/auth/${provider}/login/route.ts`, contents: renderNextHostedAuthLoginRoute(spec) },
|
|
2769
|
-
{ path: `app/api/auth/${provider}/register/route.ts`, contents: renderNextHostedAuthRegisterRoute(spec) },
|
|
2770
|
-
{ path: `app/api/auth/${provider}/callback/route.ts`, contents: renderNextHostedAuthCallbackRoute(spec) },
|
|
2771
|
-
{ path: `app/api/auth/${provider}/logout/route.ts`, contents: renderNextHostedAuthLogoutRoute(spec) }
|
|
2772
|
-
];
|
|
2773
|
-
}
|
|
2774
|
-
function renderNextStorageRoute() {
|
|
2775
|
-
return [
|
|
2776
|
-
"import { holo } from '@/server/holo'",
|
|
2777
|
-
"import { createPublicStorageResponse } from '@holo-js/storage'",
|
|
2778
|
-
"",
|
|
2779
|
-
"export async function GET(request: Request) {",
|
|
2780
|
-
" const app = await holo.getApp()",
|
|
2781
|
-
" return createPublicStorageResponse(app.projectRoot, app.config.storage, request)",
|
|
2782
|
-
"}",
|
|
2783
|
-
""
|
|
2784
|
-
].join("\n");
|
|
2785
|
-
}
|
|
2786
|
-
function renderSvelteConfig() {
|
|
2787
|
-
return [
|
|
2788
|
-
"import adapter from '@sveltejs/adapter-node'",
|
|
2789
|
-
"import { withHoloSvelteKit } from '@holo-js/adapter-sveltekit/config'",
|
|
2790
|
-
"import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'",
|
|
2791
|
-
"",
|
|
2792
|
-
"/** @type {import('@sveltejs/kit').Config} */",
|
|
2793
|
-
"const config = withHoloSvelteKit({",
|
|
2794
|
-
" preprocess: vitePreprocess(),",
|
|
2795
|
-
" kit: {",
|
|
2796
|
-
" adapter: adapter(),",
|
|
2797
|
-
" },",
|
|
2798
|
-
"})",
|
|
2799
|
-
"",
|
|
2800
|
-
"export default config",
|
|
2801
|
-
""
|
|
2802
|
-
].join("\n");
|
|
2803
|
-
}
|
|
2804
|
-
function renderSvelteUserHooks() {
|
|
2805
|
-
return [
|
|
2806
|
-
"export {}",
|
|
2807
|
-
""
|
|
2808
|
-
].join("\n");
|
|
2809
|
-
}
|
|
2810
|
-
function renderSvelteServerUserHooks() {
|
|
2811
|
-
return [
|
|
2812
|
-
"export {}",
|
|
2813
|
-
""
|
|
2814
|
-
].join("\n");
|
|
2815
|
-
}
|
|
2816
|
-
function renderSvelteViteConfig(_storageEnabled) {
|
|
2817
|
-
const externals = [
|
|
2818
|
-
" '@holo-js/adapter-sveltekit',",
|
|
2819
|
-
" '@holo-js/auth',",
|
|
2820
|
-
" '@holo-js/auth-clerk',",
|
|
2821
|
-
" '@holo-js/auth-social',",
|
|
2822
|
-
" '@holo-js/auth-workos',",
|
|
2823
|
-
" '@holo-js/authorization',",
|
|
2824
|
-
" '@holo-js/broadcast',",
|
|
2825
|
-
" '@holo-js/cache',",
|
|
2826
|
-
" '@holo-js/cache-db',",
|
|
2827
|
-
" '@holo-js/cache-redis',",
|
|
2828
|
-
" '@holo-js/config',",
|
|
2829
|
-
" '@holo-js/core',",
|
|
2830
|
-
" '@holo-js/db',",
|
|
2831
|
-
" '@holo-js/db-mysql',",
|
|
2832
|
-
" '@holo-js/db-postgres',",
|
|
2833
|
-
" '@holo-js/db-sqlite',",
|
|
2834
|
-
" '@holo-js/events',",
|
|
2835
|
-
" '@holo-js/flux',",
|
|
2836
|
-
" '@holo-js/flux-svelte',",
|
|
2837
|
-
" '@holo-js/forms',",
|
|
2838
|
-
" '@holo-js/mail',",
|
|
2839
|
-
" '@holo-js/media',",
|
|
2840
|
-
" '@holo-js/notifications',",
|
|
2841
|
-
" '@holo-js/queue',",
|
|
2842
|
-
" '@holo-js/queue-db',",
|
|
2843
|
-
" '@holo-js/queue-redis',",
|
|
2844
|
-
" '@holo-js/security',",
|
|
2845
|
-
" '@holo-js/session',",
|
|
2846
|
-
" '@holo-js/storage',",
|
|
2847
|
-
" '@holo-js/storage/runtime',",
|
|
2848
|
-
" '@holo-js/storage-s3',",
|
|
2849
|
-
" '@holo-js/validation',",
|
|
2850
|
-
" 'better-sqlite3',",
|
|
2851
|
-
" 'ioredis',",
|
|
2852
|
-
" 'mysql2',",
|
|
2853
|
-
" 'pg',"
|
|
2854
|
-
];
|
|
2855
|
-
return [
|
|
2856
|
-
"import { sveltekit } from '@sveltejs/kit/vite'",
|
|
2857
|
-
"import { defineConfig } from 'vite'",
|
|
2858
|
-
"",
|
|
2859
|
-
"export default defineConfig({",
|
|
2860
|
-
" plugins: [sveltekit()],",
|
|
2861
|
-
" server: {",
|
|
2862
|
-
" fs: {",
|
|
2863
|
-
" allow: ['.holo-js/generated'],",
|
|
2864
|
-
" },",
|
|
2865
|
-
" },",
|
|
2866
|
-
" ssr: {",
|
|
2867
|
-
" external: [",
|
|
2868
|
-
...externals,
|
|
2869
|
-
" ],",
|
|
2870
|
-
" },",
|
|
2871
|
-
"})",
|
|
2872
|
-
""
|
|
2873
|
-
].join("\n");
|
|
2874
|
-
}
|
|
2875
|
-
function renderSvelteAppHtml() {
|
|
2876
|
-
return [
|
|
2877
|
-
"<!doctype html>",
|
|
2878
|
-
'<html lang="en">',
|
|
2879
|
-
" <head>",
|
|
2880
|
-
' <meta charset="utf-8" />',
|
|
2881
|
-
' <meta name="viewport" content="width=device-width, initial-scale=1" />',
|
|
2882
|
-
" %sveltekit.head%",
|
|
2883
|
-
" </head>",
|
|
2884
|
-
' <body data-sveltekit-preload-data="hover">',
|
|
2885
|
-
' <div style="display: contents">%sveltekit.body%</div>',
|
|
2886
|
-
" </body>",
|
|
2887
|
-
"</html>",
|
|
2888
|
-
""
|
|
2889
|
-
].join("\n");
|
|
2890
|
-
}
|
|
2891
|
-
function renderSveltePage(projectName) {
|
|
2892
|
-
const escapedProjectName = escapeHtml(projectName);
|
|
2893
|
-
return [
|
|
2894
|
-
`<svelte:head><title>${escapedProjectName}</title></svelte:head>`,
|
|
2895
|
-
"",
|
|
2896
|
-
'<script lang="ts">',
|
|
2897
|
-
` const projectName = ${JSON.stringify(projectName)}`,
|
|
2898
|
-
"</script>",
|
|
2899
|
-
"",
|
|
2900
|
-
'<main class="shell">',
|
|
2901
|
-
" <h1>{projectName}</h1>",
|
|
2902
|
-
" <p>SvelteKit owns rendering. Holo owns config, discovery, and backend runtime services.</p>",
|
|
2903
|
-
"</main>",
|
|
2904
|
-
"",
|
|
2905
|
-
"<style>",
|
|
2906
|
-
" .shell {",
|
|
2907
|
-
" min-height: 100vh;",
|
|
2908
|
-
" display: grid;",
|
|
2909
|
-
" place-content: center;",
|
|
2910
|
-
" gap: 1rem;",
|
|
2911
|
-
" padding: 3rem;",
|
|
2912
|
-
" font-family: sans-serif;",
|
|
2913
|
-
" }",
|
|
2914
|
-
" h1 {",
|
|
2915
|
-
" margin: 0;",
|
|
2916
|
-
" font-size: clamp(2.5rem, 6vw, 4rem);",
|
|
2917
|
-
" }",
|
|
2918
|
-
" p {",
|
|
2919
|
-
" margin: 0;",
|
|
2920
|
-
" max-width: 40rem;",
|
|
2921
|
-
" line-height: 1.6;",
|
|
2922
|
-
" }",
|
|
2923
|
-
"</style>",
|
|
2924
|
-
""
|
|
2925
|
-
].join("\n");
|
|
2926
|
-
}
|
|
2927
|
-
function renderSvelteHoloHelper() {
|
|
2928
|
-
return [
|
|
2929
|
-
"import { createSvelteKitHoloHelpers } from '@holo-js/adapter-sveltekit'",
|
|
2930
|
-
"",
|
|
2931
|
-
"export const holo = createSvelteKitHoloHelpers()",
|
|
2932
|
-
""
|
|
2933
|
-
].join("\n");
|
|
2934
|
-
}
|
|
2935
|
-
function renderSvelteHealthRoute() {
|
|
2936
|
-
return [
|
|
2937
|
-
"import { json } from '@sveltejs/kit'",
|
|
2938
|
-
"import { holo } from '$lib/server/holo'",
|
|
2939
|
-
"",
|
|
2940
|
-
"export async function GET() {",
|
|
2941
|
-
" const app = await holo.getApp()",
|
|
2942
|
-
"",
|
|
2943
|
-
" return json({",
|
|
2944
|
-
" ok: true,",
|
|
2945
|
-
" app: app.config.app.name,",
|
|
2946
|
-
" env: app.config.app.env,",
|
|
2947
|
-
" models: app.registry?.models.length ?? 0,",
|
|
2948
|
-
" commands: app.registry?.commands.length ?? 0,",
|
|
2949
|
-
" })",
|
|
2950
|
-
"}",
|
|
2951
|
-
""
|
|
2952
|
-
].join("\n");
|
|
2953
|
-
}
|
|
2954
|
-
function renderSvelteCurrentAuthRoute() {
|
|
2955
|
-
return [
|
|
2956
|
-
"import { json } from '@sveltejs/kit'",
|
|
2957
|
-
"import auth, { check, isAuthError, provider, user } from '@holo-js/auth'",
|
|
2958
|
-
"",
|
|
2959
|
-
"export async function GET({ url }: { url: URL }) {",
|
|
2960
|
-
" const guard = url.searchParams.get('guard') ?? undefined",
|
|
2961
|
-
" try {",
|
|
2962
|
-
" const guardAuth = guard ? auth.guard(guard) : undefined",
|
|
2963
|
-
"",
|
|
2964
|
-
" return json({",
|
|
2965
|
-
" authenticated: guardAuth ? await guardAuth.check() : await check(),",
|
|
2966
|
-
" guard: guard ?? 'web',",
|
|
2967
|
-
" provider: guardAuth ? await guardAuth.provider() : await provider(),",
|
|
2968
|
-
" user: guardAuth ? await guardAuth.user() : await user(),",
|
|
2969
|
-
" })",
|
|
2970
|
-
" } catch (error) {",
|
|
2971
|
-
" if (isAuthError(error) && error.code === 'guard_not_configured') {",
|
|
2972
|
-
" return json({",
|
|
2973
|
-
" authenticated: false,",
|
|
2974
|
-
" guard: guard ?? 'web',",
|
|
2975
|
-
" provider: null,",
|
|
2976
|
-
" user: null,",
|
|
2977
|
-
" }, { status: 400 })",
|
|
2978
|
-
" }",
|
|
2979
|
-
"",
|
|
2980
|
-
" throw error",
|
|
2981
|
-
" }",
|
|
2982
|
-
"}",
|
|
2983
|
-
""
|
|
2984
|
-
].join("\n");
|
|
2985
|
-
}
|
|
2986
|
-
function renderSvelteHostedAuthLoginRoute(spec) {
|
|
2987
|
-
return [
|
|
2988
|
-
`import { ${spec.loginFunction} } from '${spec.packageName}'`,
|
|
2989
|
-
"import type { RequestHandler } from './$types'",
|
|
2990
|
-
"",
|
|
2991
|
-
"export const GET = (async (event) => {",
|
|
2992
|
-
` return await ${spec.loginFunction}(event)`,
|
|
2993
|
-
"}) satisfies RequestHandler",
|
|
2994
|
-
""
|
|
2995
|
-
].join("\n");
|
|
2996
|
-
}
|
|
2997
|
-
function renderSvelteHostedAuthRegisterRoute(spec) {
|
|
2998
|
-
return [
|
|
2999
|
-
`import { ${spec.registerFunction} } from '${spec.packageName}'`,
|
|
3000
|
-
"import type { RequestHandler } from './$types'",
|
|
3001
|
-
"",
|
|
3002
|
-
"export const GET = (async (event) => {",
|
|
3003
|
-
` return await ${spec.registerFunction}(event)`,
|
|
3004
|
-
"}) satisfies RequestHandler",
|
|
3005
|
-
""
|
|
3006
|
-
].join("\n");
|
|
3007
|
-
}
|
|
3008
|
-
function renderSvelteHostedAuthCallbackRoute(spec) {
|
|
3009
|
-
return [
|
|
3010
|
-
"import { redirect, type RequestHandler } from '@sveltejs/kit'",
|
|
3011
|
-
`import { ${spec.callbackFunction} } from '${spec.packageName}'`,
|
|
3012
|
-
"",
|
|
3013
|
-
"export const GET = (async (event) => {",
|
|
3014
|
-
` const { error } = await ${spec.callbackFunction}(event)`,
|
|
3015
|
-
" if (error) {",
|
|
3016
|
-
" throw redirect(303, `/login?error=${encodeURIComponent(error.code)}`)",
|
|
3017
|
-
" }",
|
|
3018
|
-
"",
|
|
3019
|
-
" throw redirect(303, '/')",
|
|
3020
|
-
"}) satisfies RequestHandler",
|
|
3021
|
-
""
|
|
3022
|
-
].join("\n");
|
|
3023
|
-
}
|
|
3024
|
-
function renderSvelteHostedAuthLogoutRoute(spec) {
|
|
3025
|
-
return [
|
|
3026
|
-
"import { redirect, type RequestHandler } from '@sveltejs/kit'",
|
|
3027
|
-
"import { provider } from '@holo-js/auth'",
|
|
3028
|
-
`import { ${spec.logoutFunction} } from '${spec.packageName}'`,
|
|
3029
|
-
"",
|
|
3030
|
-
"export const POST = (async (event) => {",
|
|
3031
|
-
" let currentProvider: string | null",
|
|
3032
|
-
" try {",
|
|
3033
|
-
" currentProvider = await provider()",
|
|
3034
|
-
" } catch {",
|
|
3035
|
-
" throw redirect(303, '/')",
|
|
3036
|
-
" }",
|
|
3037
|
-
"",
|
|
3038
|
-
` if (currentProvider !== '${spec.provider}') {`,
|
|
3039
|
-
" throw redirect(303, '/')",
|
|
3040
|
-
" }",
|
|
3041
|
-
"",
|
|
3042
|
-
` const { data, error } = await ${spec.logoutFunction}(event)`,
|
|
3043
|
-
" if (error) {",
|
|
3044
|
-
" return Response.json({ data, error }, { status: error.status })",
|
|
3045
|
-
" }",
|
|
3046
|
-
"",
|
|
3047
|
-
" throw redirect(303, data.url)",
|
|
3048
|
-
"}) satisfies RequestHandler",
|
|
3049
|
-
""
|
|
3050
|
-
].join("\n");
|
|
3051
|
-
}
|
|
3052
|
-
function renderSvelteHostedAuthRouteFiles(provider) {
|
|
3053
|
-
const spec = HOSTED_AUTH_PROVIDERS[provider];
|
|
3054
|
-
return [
|
|
3055
|
-
{ path: `src/routes/api/auth/${provider}/login/+server.ts`, contents: renderSvelteHostedAuthLoginRoute(spec) },
|
|
3056
|
-
{ path: `src/routes/api/auth/${provider}/register/+server.ts`, contents: renderSvelteHostedAuthRegisterRoute(spec) },
|
|
3057
|
-
{ path: `src/routes/api/auth/${provider}/callback/+server.ts`, contents: renderSvelteHostedAuthCallbackRoute(spec) },
|
|
3058
|
-
{ path: `src/routes/api/auth/${provider}/logout/+server.ts`, contents: renderSvelteHostedAuthLogoutRoute(spec) }
|
|
3059
|
-
];
|
|
3060
|
-
}
|
|
3061
|
-
function renderAuthProviderRouteFiles(framework, features) {
|
|
3062
|
-
return getRequestedHostedAuthProviders(features).flatMap((provider) => {
|
|
3063
|
-
if (framework === "nuxt") {
|
|
3064
|
-
return renderNuxtHostedAuthRouteFiles(provider);
|
|
3065
|
-
}
|
|
3066
|
-
if (framework === "next") {
|
|
3067
|
-
return renderNextHostedAuthRouteFiles(provider);
|
|
3068
|
-
}
|
|
3069
|
-
return renderSvelteHostedAuthRouteFiles(provider);
|
|
3070
|
-
});
|
|
3071
|
-
}
|
|
3072
|
-
function renderSvelteStorageRoute() {
|
|
3073
|
-
return [
|
|
3074
|
-
"import { holo } from '$lib/server/holo'",
|
|
3075
|
-
"import { createPublicStorageResponse } from '@holo-js/storage'",
|
|
3076
|
-
"",
|
|
3077
|
-
"export async function GET({ request }: { request: Request }) {",
|
|
3078
|
-
" const app = await holo.getApp()",
|
|
3079
|
-
" return createPublicStorageResponse(app.projectRoot, app.config.storage, request)",
|
|
3080
|
-
"}",
|
|
3081
|
-
""
|
|
3082
|
-
].join("\n");
|
|
3083
|
-
}
|
|
3084
|
-
function renderFrameworkFiles(options) {
|
|
3085
|
-
const optionalPackages = normalizeScaffoldOptionalPackages(options.optionalPackages);
|
|
3086
|
-
const storageEnabled = optionalPackages.includes("storage");
|
|
3087
|
-
const authEnabled = optionalPackages.includes("auth");
|
|
3088
|
-
if (options.framework === "nuxt") {
|
|
3089
|
-
return [
|
|
3090
|
-
{ path: "app/app.vue", contents: renderNuxtAppVue(options.projectName) },
|
|
3091
|
-
{ path: "nuxt.config.ts", contents: renderNuxtConfig() },
|
|
3092
|
-
{ path: "server/api/holo/health.get.ts", contents: renderNuxtHealthRoute() },
|
|
3093
|
-
{ path: "shared/.gitkeep", contents: "" },
|
|
3094
|
-
...authEnabled ? [
|
|
3095
|
-
{ path: "server/api/auth/user.get.ts", contents: renderNuxtCurrentAuthRoute() }
|
|
3096
|
-
] : []
|
|
3097
|
-
];
|
|
3098
|
-
}
|
|
3099
|
-
if (options.framework === "next") {
|
|
3100
|
-
return [
|
|
3101
|
-
{ path: "next.config.ts", contents: renderNextConfig() },
|
|
3102
|
-
{ path: "next-env.d.ts", contents: renderNextEnvDts() },
|
|
3103
|
-
{ path: "app/layout.tsx", contents: renderNextLayout(options.projectName) },
|
|
3104
|
-
{ path: "app/page.tsx", contents: renderNextPage(options.projectName) },
|
|
3105
|
-
{ path: "app/api/holo/health/route.ts", contents: renderNextHealthRoute() },
|
|
3106
|
-
...authEnabled ? [
|
|
3107
|
-
{ path: "app/api/auth/user/route.ts", contents: renderNextCurrentAuthRoute() }
|
|
3108
|
-
] : [],
|
|
3109
|
-
...storageEnabled ? [{ path: "app/storage/[[...path]]/route.ts", contents: renderNextStorageRoute() }] : [],
|
|
3110
|
-
{ path: "server/holo.ts", contents: renderNextHoloHelper() },
|
|
3111
|
-
{ path: "instrumentation.ts", contents: renderNextInstrumentation() }
|
|
3112
|
-
];
|
|
3113
|
-
}
|
|
3114
|
-
return [
|
|
3115
|
-
{ path: "svelte.config.js", contents: renderSvelteConfig() },
|
|
3116
|
-
{ path: "vite.config.ts", contents: renderSvelteViteConfig(storageEnabled) },
|
|
3117
|
-
{ path: "src/hooks.ts", contents: renderSvelteUserHooks() },
|
|
3118
|
-
{ path: "src/hooks.server.ts", contents: renderSvelteServerUserHooks() },
|
|
3119
|
-
{ path: "src/app.html", contents: renderSvelteAppHtml() },
|
|
3120
|
-
{ path: "src/routes/+page.svelte", contents: renderSveltePage(options.projectName) },
|
|
3121
|
-
{ path: "src/routes/api/holo/health/+server.ts", contents: renderSvelteHealthRoute() },
|
|
3122
|
-
...authEnabled ? [
|
|
3123
|
-
{ path: "src/routes/api/auth/user/+server.ts", contents: renderSvelteCurrentAuthRoute() }
|
|
3124
|
-
] : [],
|
|
3125
|
-
...storageEnabled ? [{ path: "src/routes/storage/[...path]/+server.ts", contents: renderSvelteStorageRoute() }] : [],
|
|
3126
|
-
{ path: "src/lib/server/holo.ts", contents: renderSvelteHoloHelper() }
|
|
3127
|
-
];
|
|
3128
|
-
}
|
|
3129
|
-
function renderFrameworkRunner(options) {
|
|
3130
|
-
const commandName = options.framework === "nuxt" ? "nuxt" : options.framework === "next" ? "next" : "vite";
|
|
3131
|
-
return [
|
|
3132
|
-
"import { existsSync, readFileSync, readlinkSync } from 'node:fs'",
|
|
3133
|
-
"import { dirname, resolve } from 'node:path'",
|
|
3134
|
-
"import { fileURLToPath, pathToFileURL } from 'node:url'",
|
|
3135
|
-
"import { execFileSync, spawn } from 'node:child_process'",
|
|
3136
|
-
"",
|
|
3137
|
-
"const mode = process.argv[2]",
|
|
3138
|
-
"const manifestPath = fileURLToPath(new URL('./project.json', import.meta.url))",
|
|
3139
|
-
"const projectRoot = resolve(dirname(manifestPath), '../..')",
|
|
3140
|
-
"const runtimeSchemaPath = resolve(projectRoot, '.holo-js/generated/schema.mjs')",
|
|
3141
|
-
"const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))",
|
|
3142
|
-
"const framework = String(manifest.framework ?? '')",
|
|
3143
|
-
`const commandName = ${JSON.stringify(commandName)}`,
|
|
3144
|
-
"const commandArgs = mode === 'dev'",
|
|
3145
|
-
" ? ['dev']",
|
|
3146
|
-
" : mode === 'build'",
|
|
3147
|
-
" ? framework === 'sveltekit' ? ['build', '--logLevel', 'error'] : ['build']",
|
|
3148
|
-
" : undefined",
|
|
3149
|
-
"",
|
|
3150
|
-
"if (!commandArgs) {",
|
|
3151
|
-
" console.error(`[holo] Unknown framework runner mode: ${String(mode)}`)",
|
|
3152
|
-
" process.exit(1)",
|
|
3153
|
-
"}",
|
|
3154
|
-
"",
|
|
3155
|
-
"const binaryPath = resolve(",
|
|
3156
|
-
" projectRoot,",
|
|
3157
|
-
" 'node_modules',",
|
|
3158
|
-
" '.bin',",
|
|
3159
|
-
" process.platform === 'win32' ? `${commandName}.cmd` : commandName,",
|
|
3160
|
-
")",
|
|
3161
|
-
"",
|
|
3162
|
-
"const suppressedOutput = framework === 'sveltekit'",
|
|
3163
|
-
" ? new Set([",
|
|
3164
|
-
` '"try_get_request_store" is imported from external module "@sveltejs/kit/internal/server" but never used in ".svelte-kit/adapter-node/index.js".',`,
|
|
3165
|
-
" ])",
|
|
3166
|
-
" : new Set()",
|
|
3167
|
-
"",
|
|
3168
|
-
"function shouldSuppressOutput(line) {",
|
|
3169
|
-
" if (suppressedOutput.has(line)) {",
|
|
3170
|
-
" return true",
|
|
3171
|
-
" }",
|
|
3172
|
-
"",
|
|
3173
|
-
" return framework === 'sveltekit'",
|
|
3174
|
-
" && line.startsWith('Circular dependency: ')",
|
|
3175
|
-
" && line.includes('/node_modules/semver/')",
|
|
3176
|
-
"}",
|
|
3177
|
-
"",
|
|
3178
|
-
"function pipeOutput(stream, target, onLine) {",
|
|
3179
|
-
" if (!stream) {",
|
|
3180
|
-
" return",
|
|
3181
|
-
" }",
|
|
3182
|
-
"",
|
|
3183
|
-
" let buffered = ''",
|
|
3184
|
-
" stream.on('data', (chunk) => {",
|
|
3185
|
-
" buffered += chunk.toString()",
|
|
3186
|
-
" const lines = buffered.split(/\\r?\\n/)",
|
|
3187
|
-
" buffered = lines.pop() ?? ''",
|
|
3188
|
-
" for (const line of lines) {",
|
|
3189
|
-
" onLine?.(line)",
|
|
3190
|
-
" if (!shouldSuppressOutput(line)) {",
|
|
3191
|
-
" target.write(`${line}\\n`)",
|
|
3192
|
-
" }",
|
|
3193
|
-
" }",
|
|
3194
|
-
" })",
|
|
3195
|
-
"",
|
|
3196
|
-
" stream.on('end', () => {",
|
|
3197
|
-
" if (buffered.length > 0) {",
|
|
3198
|
-
" onLine?.(buffered)",
|
|
3199
|
-
" }",
|
|
3200
|
-
" if (buffered.length > 0 && !shouldSuppressOutput(buffered)) {",
|
|
3201
|
-
" target.write(buffered)",
|
|
3202
|
-
" }",
|
|
3203
|
-
" })",
|
|
3204
|
-
"}",
|
|
3205
|
-
"",
|
|
3206
|
-
"function extractNextConflictInfo(lines) {",
|
|
3207
|
-
" if (framework !== 'next' || mode !== 'dev') {",
|
|
3208
|
-
" return undefined",
|
|
3209
|
-
" }",
|
|
3210
|
-
"",
|
|
3211
|
-
" if (!lines.some(line => line.includes('Another next dev server is already running.'))) {",
|
|
3212
|
-
" return undefined",
|
|
3213
|
-
" }",
|
|
3214
|
-
"",
|
|
3215
|
-
" let pid",
|
|
3216
|
-
" let dir",
|
|
3217
|
-
"",
|
|
3218
|
-
" for (const line of lines) {",
|
|
3219
|
-
" const match = line.match(/^- PID:\\s+(\\d+)\\s*$/)",
|
|
3220
|
-
" if (match) {",
|
|
3221
|
-
" pid = Number.parseInt(match[1], 10)",
|
|
3222
|
-
" continue",
|
|
3223
|
-
" }",
|
|
3224
|
-
"",
|
|
3225
|
-
" const dirMatch = line.match(/^- Dir:\\s+(.+?)\\s*$/)",
|
|
3226
|
-
" if (dirMatch) {",
|
|
3227
|
-
" dir = dirMatch[1]",
|
|
3228
|
-
" }",
|
|
3229
|
-
" }",
|
|
3230
|
-
"",
|
|
3231
|
-
" return typeof pid === 'number' ? { pid, dir } : undefined",
|
|
3232
|
-
"}",
|
|
3233
|
-
"",
|
|
3234
|
-
"async function waitForProcessExit(pid, timeoutMs = 5000) {",
|
|
3235
|
-
" const deadline = Date.now() + timeoutMs",
|
|
3236
|
-
" while (Date.now() < deadline) {",
|
|
3237
|
-
" try {",
|
|
3238
|
-
" process.kill(pid, 0)",
|
|
3239
|
-
" } catch (error) {",
|
|
3240
|
-
" if (error && typeof error === 'object' && 'code' in error && error.code === 'ESRCH') {",
|
|
3241
|
-
" return true",
|
|
3242
|
-
" }",
|
|
3243
|
-
" throw error",
|
|
3244
|
-
" }",
|
|
3245
|
-
"",
|
|
3246
|
-
" await new Promise(resolve => setTimeout(resolve, 100))",
|
|
3247
|
-
" }",
|
|
3248
|
-
"",
|
|
3249
|
-
" return false",
|
|
3250
|
-
"}",
|
|
3251
|
-
"",
|
|
3252
|
-
"function inspectProcess(pid) {",
|
|
3253
|
-
" try {",
|
|
3254
|
-
" if (process.platform === 'linux' && existsSync(`/proc/${pid}`)) {",
|
|
3255
|
-
" return {",
|
|
3256
|
-
" cwd: readlinkSync(`/proc/${pid}/cwd`),",
|
|
3257
|
-
" args: readFileSync(`/proc/${pid}/cmdline`, 'utf8').replaceAll('\\u0000', ' ').trim(),",
|
|
3258
|
-
" }",
|
|
3259
|
-
" }",
|
|
3260
|
-
" } catch {",
|
|
3261
|
-
" // Fall through to the portable process inspection path below.",
|
|
3262
|
-
" }",
|
|
3263
|
-
"",
|
|
3264
|
-
" try {",
|
|
3265
|
-
" return {",
|
|
3266
|
-
" args: execFileSync('ps', ['-p', String(pid), '-o', 'args='], {",
|
|
3267
|
-
" encoding: 'utf8',",
|
|
3268
|
-
" }).trim(),",
|
|
3269
|
-
" }",
|
|
3270
|
-
" } catch {",
|
|
3271
|
-
" return undefined",
|
|
3272
|
-
" }",
|
|
3273
|
-
"}",
|
|
3274
|
-
"",
|
|
3275
|
-
"function isOwnedNextDevServer(pid, reportedDir) {",
|
|
3276
|
-
" const expectedDir = typeof reportedDir === 'string' ? resolve(reportedDir) : undefined",
|
|
3277
|
-
" if (expectedDir && expectedDir !== projectRoot) {",
|
|
3278
|
-
" return false",
|
|
3279
|
-
" }",
|
|
3280
|
-
"",
|
|
3281
|
-
" const details = inspectProcess(pid)",
|
|
3282
|
-
" if (!details) {",
|
|
3283
|
-
" return expectedDir === projectRoot",
|
|
3284
|
-
" }",
|
|
3285
|
-
"",
|
|
3286
|
-
" const argsMatch = details.args.includes('next') && details.args.includes('dev')",
|
|
3287
|
-
" const cwdMatches = typeof details.cwd === 'string' && resolve(details.cwd) === projectRoot",
|
|
3288
|
-
" const argsReferenceProject = details.args.includes(projectRoot)",
|
|
3289
|
-
"",
|
|
3290
|
-
" return argsMatch && (cwdMatches || argsReferenceProject || expectedDir === projectRoot)",
|
|
3291
|
-
"}",
|
|
3292
|
-
"",
|
|
3293
|
-
"async function stopStaleNextDevServer(pid, reportedDir, force = false) {",
|
|
3294
|
-
" if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) {",
|
|
3295
|
-
" return false",
|
|
3296
|
-
" }",
|
|
3297
|
-
"",
|
|
3298
|
-
" if (!isOwnedNextDevServer(pid, reportedDir)) {",
|
|
3299
|
-
" return false",
|
|
3300
|
-
" }",
|
|
3301
|
-
"",
|
|
3302
|
-
" if (!force) {",
|
|
3303
|
-
" return false",
|
|
3304
|
-
" }",
|
|
3305
|
-
"",
|
|
3306
|
-
" try {",
|
|
3307
|
-
" process.kill(pid, 'SIGTERM')",
|
|
3308
|
-
" } catch (error) {",
|
|
3309
|
-
" if (error && typeof error === 'object' && 'code' in error && error.code === 'ESRCH') {",
|
|
3310
|
-
" return true",
|
|
3311
|
-
" }",
|
|
3312
|
-
" return false",
|
|
3313
|
-
" }",
|
|
3314
|
-
"",
|
|
3315
|
-
" return waitForProcessExit(pid)",
|
|
3316
|
-
"}",
|
|
3317
|
-
"",
|
|
3318
|
-
"if (!existsSync(binaryPath)) {",
|
|
3319
|
-
' console.error(`[holo] Missing framework binary "${commandName}" for "${framework}". Run your package manager install first.`)',
|
|
3320
|
-
" process.exit(1)",
|
|
3321
|
-
"}",
|
|
3322
|
-
"",
|
|
3323
|
-
"let child = null",
|
|
3324
|
-
"let forwardedSignal = null",
|
|
3325
|
-
"",
|
|
3326
|
-
"function detachSignalForwarders() {",
|
|
3327
|
-
" process.removeListener('SIGINT', onSigint)",
|
|
3328
|
-
" process.removeListener('SIGTERM', onSigterm)",
|
|
3329
|
-
"}",
|
|
3330
|
-
"",
|
|
3331
|
-
"function forwardSignal(signal) {",
|
|
3332
|
-
" if (forwardedSignal || !child || child.exitCode !== null) {",
|
|
3333
|
-
" return",
|
|
3334
|
-
" }",
|
|
3335
|
-
"",
|
|
3336
|
-
" forwardedSignal = signal",
|
|
3337
|
-
" child.kill(signal)",
|
|
3338
|
-
"}",
|
|
3339
|
-
"",
|
|
3340
|
-
"function onSigint() {",
|
|
3341
|
-
" detachSignalForwarders()",
|
|
3342
|
-
" forwardSignal('SIGINT')",
|
|
3343
|
-
"}",
|
|
3344
|
-
"",
|
|
3345
|
-
"function onSigterm() {",
|
|
3346
|
-
" detachSignalForwarders()",
|
|
3347
|
-
" forwardSignal('SIGTERM')",
|
|
3348
|
-
"}",
|
|
3349
|
-
"",
|
|
3350
|
-
"process.on('SIGINT', onSigint)",
|
|
3351
|
-
"process.on('SIGTERM', onSigterm)",
|
|
3352
|
-
"",
|
|
3353
|
-
"async function run() {",
|
|
3354
|
-
" let restartedAfterConflict = false",
|
|
3355
|
-
" const maxStderrLines = 200",
|
|
3356
|
-
"",
|
|
3357
|
-
" while (true) {",
|
|
3358
|
-
" const stderrLines = []",
|
|
3359
|
-
" const childEnv = { ...process.env }",
|
|
3360
|
-
" if (existsSync(runtimeSchemaPath)) {",
|
|
3361
|
-
" const preload = `--import=${pathToFileURL(runtimeSchemaPath).href}`",
|
|
3362
|
-
" childEnv.NODE_OPTIONS = childEnv.NODE_OPTIONS",
|
|
3363
|
-
" ? `${childEnv.NODE_OPTIONS} ${preload}`",
|
|
3364
|
-
" : preload",
|
|
3365
|
-
" }",
|
|
3366
|
-
" child = spawn(binaryPath, commandArgs, {",
|
|
3367
|
-
" cwd: projectRoot,",
|
|
3368
|
-
" env: childEnv,",
|
|
3369
|
-
" stdio: ['inherit', 'pipe', 'pipe'],",
|
|
3370
|
-
" })",
|
|
3371
|
-
" forwardedSignal = null",
|
|
3372
|
-
"",
|
|
3373
|
-
" pipeOutput(child.stdout, process.stdout)",
|
|
3374
|
-
" pipeOutput(child.stderr, process.stderr, line => {",
|
|
3375
|
-
" if (stderrLines.length >= maxStderrLines) {",
|
|
3376
|
-
" stderrLines.shift()",
|
|
3377
|
-
" }",
|
|
3378
|
-
" stderrLines.push(line)",
|
|
3379
|
-
" })",
|
|
3380
|
-
"",
|
|
3381
|
-
" const result = await new Promise((resolve, reject) => {",
|
|
3382
|
-
" child.on('error', reject)",
|
|
3383
|
-
" child.on('close', (code, signal) => resolve({ code, signal }))",
|
|
3384
|
-
" })",
|
|
3385
|
-
"",
|
|
3386
|
-
" if (result.code === 0) {",
|
|
3387
|
-
" process.exit(0)",
|
|
3388
|
-
" }",
|
|
3389
|
-
"",
|
|
3390
|
-
" const conflictInfo = extractNextConflictInfo(stderrLines)",
|
|
3391
|
-
" if (!restartedAfterConflict && conflictInfo) {",
|
|
3392
|
-
" const stopped = await stopStaleNextDevServer(conflictInfo.pid, conflictInfo.dir)",
|
|
3393
|
-
" if (stopped) {",
|
|
3394
|
-
" restartedAfterConflict = true",
|
|
3395
|
-
" console.error(`[holo] Stopped stale Next dev server ${conflictInfo.pid}. Restarting dev server.`)",
|
|
3396
|
-
" continue",
|
|
3397
|
-
" }",
|
|
3398
|
-
"",
|
|
3399
|
-
" // Another dev server is already running (possibly in a different directory).",
|
|
3400
|
-
" // Next.js already printed the conflict message with instructions to kill it.",
|
|
3401
|
-
" // Exit gracefully to avoid noisy npm/bun error cascades.",
|
|
3402
|
-
" process.exit(0)",
|
|
3403
|
-
" }",
|
|
3404
|
-
"",
|
|
3405
|
-
" if (result.signal) {",
|
|
3406
|
-
" detachSignalForwarders()",
|
|
3407
|
-
" process.kill(process.pid, result.signal)",
|
|
3408
|
-
" } else {",
|
|
3409
|
-
" process.exit(result.code ?? 1)",
|
|
3410
|
-
" }",
|
|
3411
|
-
" }",
|
|
3412
|
-
"}",
|
|
3413
|
-
"",
|
|
3414
|
-
"run().catch((error) => {",
|
|
3415
|
-
" console.error(error instanceof Error ? error.message : String(error))",
|
|
3416
|
-
" process.exit(1)",
|
|
3417
|
-
"})",
|
|
3418
|
-
""
|
|
3419
|
-
].join("\n");
|
|
3420
|
-
}
|
|
3421
|
-
|
|
3422
2348
|
// src/project/scaffold/framework.ts
|
|
3423
2349
|
function resolvePackageManagerVersion(value) {
|
|
3424
2350
|
return SCAFFOLD_PACKAGE_MANAGER_VERSIONS[value];
|
|
@@ -3759,6 +2685,25 @@ async function syncHostedAuthRouteFiles(projectRoot, features) {
|
|
|
3759
2685
|
await mkdir3(dirname2(targetPath), { recursive: true });
|
|
3760
2686
|
await writeTextFile(targetPath, file.contents);
|
|
3761
2687
|
}
|
|
2688
|
+
if (framework === "next") {
|
|
2689
|
+
for (const file of renderNextManagedHostedAuthRouteFiles(features)) {
|
|
2690
|
+
await writeTextFile(resolve4(projectRoot, file.path), file.contents);
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
async function syncAuthRouteFiles(projectRoot) {
|
|
2695
|
+
const { dependencies, devDependencies } = await readPackageJsonDependencyState(projectRoot);
|
|
2696
|
+
const framework = detectProjectFrameworkFromPackageJson(dependencies, devDependencies);
|
|
2697
|
+
if (!framework) {
|
|
2698
|
+
return;
|
|
2699
|
+
}
|
|
2700
|
+
for (const file of renderAuthRouteFiles(framework)) {
|
|
2701
|
+
const targetPath = resolve4(projectRoot, file.path);
|
|
2702
|
+
if (await pathExists(targetPath)) {
|
|
2703
|
+
continue;
|
|
2704
|
+
}
|
|
2705
|
+
await writeTextFile(targetPath, file.contents);
|
|
2706
|
+
}
|
|
3762
2707
|
}
|
|
3763
2708
|
async function installAuthIntoProject(projectRoot, features = {}) {
|
|
3764
2709
|
const project = await loadProjectConfig(projectRoot);
|
|
@@ -3810,6 +2755,7 @@ async function installAuthIntoProject(projectRoot, features = {}) {
|
|
|
3810
2755
|
}
|
|
3811
2756
|
const createdCorsConfig2 = await ensureCorsConfigFile(projectRoot);
|
|
3812
2757
|
await syncBroadcastAuthSupportAfterAuthInstall(projectRoot);
|
|
2758
|
+
await syncAuthRouteFiles(projectRoot);
|
|
3813
2759
|
await syncHostedAuthRouteFiles(projectRoot, nextAuthFeatures);
|
|
3814
2760
|
return {
|
|
3815
2761
|
updatedPackageJson: await upsertAuthPackageDependencies(projectRoot, nextAuthFeatures),
|
|
@@ -3876,6 +2822,7 @@ async function installAuthIntoProject(projectRoot, features = {}) {
|
|
|
3876
2822
|
await writeTextFile(envExamplePath, nextEnvExample.contents);
|
|
3877
2823
|
}
|
|
3878
2824
|
await syncBroadcastAuthSupportAfterAuthInstall(projectRoot);
|
|
2825
|
+
await syncAuthRouteFiles(projectRoot);
|
|
3879
2826
|
await syncHostedAuthRouteFiles(projectRoot, features);
|
|
3880
2827
|
return {
|
|
3881
2828
|
updatedPackageJson: await upsertAuthPackageDependencies(projectRoot, features),
|
|
@@ -4052,7 +2999,7 @@ async function installMediaIntoProject(projectRoot) {
|
|
|
4052
2999
|
if (!mediaConfigPath) {
|
|
4053
3000
|
await writeTextFile(resolve4(projectRoot, "config/media.ts"), renderMediaConfig());
|
|
4054
3001
|
}
|
|
4055
|
-
const { createMediaTableMigration } = await import("./media-migrations-
|
|
3002
|
+
const { createMediaTableMigration } = await import("./media-migrations-RBF4QZZ5.mjs");
|
|
4056
3003
|
const migrationFilePath = await createMediaTableMigration(projectRoot, {
|
|
4057
3004
|
skipIfExists: true
|
|
4058
3005
|
});
|
|
@@ -4241,9 +3188,6 @@ export {
|
|
|
4241
3188
|
renderScaffoldGitignore,
|
|
4242
3189
|
renderScaffoldTsconfig,
|
|
4243
3190
|
renderVSCodeSettings,
|
|
4244
|
-
renderAuthProviderRouteFiles,
|
|
4245
|
-
renderFrameworkFiles,
|
|
4246
|
-
renderFrameworkRunner,
|
|
4247
3191
|
resolvePackageManagerVersion,
|
|
4248
3192
|
renderScaffoldPackageJson,
|
|
4249
3193
|
scaffoldProject,
|