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