@holo-js/adapter-nuxt 0.3.28 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/.holo-build-fingerprint +1 -0
- package/dist/module.d.mts +7 -11
- package/dist/module.json +2 -2
- package/dist/module.mjs +103 -57
- package/dist/runtime/authorization-error.js +1 -1
- package/dist/runtime/composables/index.d.ts +2 -4
- package/dist/runtime/composables/index.js +34 -37
- package/dist/runtime/plugins/forms.js +1 -1
- package/dist/runtime/plugins/init.js +23 -7
- package/dist/runtime/server/connection-address.js +7 -0
- package/dist/runtime/server/error.js +6 -15
- package/dist/runtime/server/form-failure.js +19 -6
- package/dist/runtime/server/route-request.js +39 -0
- package/dist/runtime/server/routes/broadcast-auth.post.js +3 -13
- package/dist/runtime/server/routes/broadcast-config.get.js +1 -1
- package/dist/runtime/server/routes/realtime-mutation.post.js +6 -5
- package/dist/runtime/server/routes/realtime-query.post.js +6 -5
- package/dist/runtime/server/routes/storage.js +8 -2
- package/dist/runtime/shims.d.ts +3 -50
- package/package.json +31 -24
- package/dist/runtime/drivers/s3.js +0 -1
- package/dist/runtime/plugins/storage.js +0 -10
- package/dist/runtime/server/routes/realtime-request.d.ts +0 -0
- package/dist/runtime/server/routes/realtime-request.js +0 -14
- /package/dist/runtime/{drivers/s3.d.ts → server/connection-address.d.ts} +0 -0
- /package/dist/runtime/{plugins/storage.d.ts → server/route-request.d.ts} +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Holo-JS contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
e7de7522096e85f09dffe635f153a2321b5d38f460190ac2164bb8db98bd3188
|
package/dist/module.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { NuxtModule } from 'nuxt/schema';
|
|
2
2
|
import { LoadedHoloConfig, HoloConfigMap } from '@holo-js/config';
|
|
3
3
|
|
|
4
4
|
type ModuleOptions = Record<string, never>;
|
|
@@ -44,7 +44,6 @@ type HoloStorageRuntimeConfig = {
|
|
|
44
44
|
disks: Record<string, RuntimeDiskConfig>;
|
|
45
45
|
};
|
|
46
46
|
type StorageModule = {
|
|
47
|
-
applyNitroStorageConfig(nitro: NuxtOptionsWithNitro, config: HoloStorageRuntimeConfig, s3Driver: string): void;
|
|
48
47
|
hasPublicLocalDisk(config: HoloStorageRuntimeConfig): boolean;
|
|
49
48
|
mergeModuleOptions(base: StorageModuleOptions | undefined, overrides: StorageModuleOptions | undefined): StorageModuleOptions;
|
|
50
49
|
normalizeModuleOptions(options: StorageModuleOptions | undefined): HoloStorageRuntimeConfig;
|
|
@@ -65,7 +64,6 @@ type NuxtViteOptions = {
|
|
|
65
64
|
};
|
|
66
65
|
interface NuxtOptionsWithNitro {
|
|
67
66
|
nitro: {
|
|
68
|
-
storage: Record<string, unknown>;
|
|
69
67
|
moduleSideEffects?: string[];
|
|
70
68
|
errorHandler?: string | string[];
|
|
71
69
|
externals?: {
|
|
@@ -94,7 +92,6 @@ interface NuxtOptionsWithNitro {
|
|
|
94
92
|
};
|
|
95
93
|
[key: string]: unknown;
|
|
96
94
|
};
|
|
97
|
-
holoStorage?: HoloStorageRuntimeConfig;
|
|
98
95
|
[key: string]: unknown;
|
|
99
96
|
};
|
|
100
97
|
build: {
|
|
@@ -106,7 +103,6 @@ interface NuxtOptionsWithNitro {
|
|
|
106
103
|
rootDir?: string;
|
|
107
104
|
_holoStorageModuleOptions?: StorageModuleOptions;
|
|
108
105
|
_holoStorageFinalizeRegistered?: boolean;
|
|
109
|
-
_holoStorageRuntimeRegistered?: boolean;
|
|
110
106
|
_holoBroadcastAuthRouteRegistered?: boolean;
|
|
111
107
|
_holoRealtimeRouteRegistered?: boolean;
|
|
112
108
|
_holoClientTransformsRegistered?: boolean;
|
|
@@ -120,10 +116,11 @@ declare function isProjectPackageLinked(rootDir: string, packageRootName: string
|
|
|
120
116
|
declare function addNitroRuntimeTraceIncludes(opts: NuxtOptionsWithNitro, rootDir: string, pluginPackages?: readonly string[], resolvePackage?: (rootDir: string, packageName: string) => string | undefined): void;
|
|
121
117
|
declare function resolveClientOptimizeDeps(rootDir: string): string[];
|
|
122
118
|
declare function addViteOptimizeDeps(opts: NuxtOptionsWithNitro, deps: readonly string[]): void;
|
|
123
|
-
declare function isRealtimeDefinitionModule(
|
|
119
|
+
declare function isRealtimeDefinitionModule(rootDir: string, id: string, directories?: readonly string[]): boolean;
|
|
124
120
|
declare function resolveExistingRealtimeDefinitionFile(path: string): string | undefined;
|
|
125
121
|
declare function existsFile(path: string): boolean;
|
|
126
122
|
declare function resolveRealtimeDefinitionImport(source: string, importer: string | undefined): string | undefined;
|
|
123
|
+
declare function renderRealtimeDefinitionsModule(rootDir: string): string;
|
|
127
124
|
declare function createRealtimeDefinitionVitePlugin(rootDir: string): unknown;
|
|
128
125
|
declare function addVitePlugin(vite: NuxtViteOptions, plugin: unknown): void;
|
|
129
126
|
declare function addNitroErrorHandler(opts: NuxtOptionsWithNitro, handler: string): void;
|
|
@@ -136,9 +133,8 @@ declare function resolveStorageSetup(storageModule: StorageModule | undefined, l
|
|
|
136
133
|
};
|
|
137
134
|
declare function finalizeStorageSetup(storageModule: StorageModule, opts: NuxtOptionsWithNitro, resolver: {
|
|
138
135
|
resolve(path: string): string;
|
|
139
|
-
}
|
|
140
|
-
declare
|
|
141
|
-
declare const _default: nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
136
|
+
}): void;
|
|
137
|
+
declare const holoNuxtModule: NuxtModule<ModuleOptions>;
|
|
142
138
|
|
|
143
139
|
declare const moduleInternals: {
|
|
144
140
|
addViteOptimizeDeps: typeof addViteOptimizeDeps;
|
|
@@ -151,7 +147,6 @@ declare const moduleInternals: {
|
|
|
151
147
|
hasModuleNotFoundCode: typeof hasModuleNotFoundCode;
|
|
152
148
|
hasLoadedConfigFile: typeof hasLoadedConfigFile;
|
|
153
149
|
importOptionalStorageS3Module: typeof importOptionalStorageS3Module;
|
|
154
|
-
applyStorageRuntimeConfig: typeof applyStorageRuntimeConfig;
|
|
155
150
|
finalizeStorageSetup: typeof finalizeStorageSetup;
|
|
156
151
|
resolveStorageSetup: typeof resolveStorageSetup;
|
|
157
152
|
isModuleResolutionFailure: typeof isModuleResolutionFailure;
|
|
@@ -160,6 +155,7 @@ declare const moduleInternals: {
|
|
|
160
155
|
isProjectPackageLinked: typeof isProjectPackageLinked;
|
|
161
156
|
isRealtimeDefinitionModule: typeof isRealtimeDefinitionModule;
|
|
162
157
|
resolveClientOptimizeDeps: typeof resolveClientOptimizeDeps;
|
|
158
|
+
renderRealtimeDefinitionsModule: typeof renderRealtimeDefinitionsModule;
|
|
163
159
|
resolveExistingRealtimeDefinitionFile: typeof resolveExistingRealtimeDefinitionFile;
|
|
164
160
|
resolveRealtimeDefinitionImport: typeof resolveRealtimeDefinitionImport;
|
|
165
161
|
};
|
|
@@ -167,5 +163,5 @@ declare const adapterNuxtInternals: {
|
|
|
167
163
|
toStorageModuleOptions: typeof toStorageModuleOptions;
|
|
168
164
|
};
|
|
169
165
|
|
|
170
|
-
export { adapterNuxtInternals,
|
|
166
|
+
export { adapterNuxtInternals, holoNuxtModule as default, moduleInternals };
|
|
171
167
|
export type { ModuleOptions };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import { createRealtimeClientDefinitionTransform as createRealtimeClientDefinitionTransform$1, createRealtimeClientDefinitionModule as createRealtimeClientDefinitionModule$1, compileBrowserValidation } from '@holo-js/adapter-shared/build';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { resolveRealtimeDefinitionDirectories, isInRealtimeDefinitionDirectory, assertRealtimeDefinitionTree } from '@holo-js/adapter-shared/realtime-directories';
|
|
3
|
+
import { existsSync, lstatSync, readFileSync, readdirSync } from 'node:fs';
|
|
4
|
+
import { readdir, readFile, mkdir, access } from 'node:fs/promises';
|
|
4
5
|
import { createRequire } from 'node:module';
|
|
5
6
|
import { resolve, extname, relative, basename, dirname, isAbsolute } from 'node:path';
|
|
6
7
|
import { defineNuxtModule, createResolver, addServerPlugin, addServerImportsDir, addServerHandler, addPlugin } from '@nuxt/kit';
|
|
7
8
|
import { loadConfigDirectory } from '@holo-js/config';
|
|
8
9
|
import '@holo-js/db/config';
|
|
9
10
|
|
|
10
|
-
function createRealtimeClientDefinitionModule(source) {
|
|
11
|
-
return createRealtimeClientDefinitionModule$1(source, "@holo-js/adapter-nuxt/realtime");
|
|
11
|
+
function createRealtimeClientDefinitionModule(source, filePath) {
|
|
12
|
+
return createRealtimeClientDefinitionModule$1({ source, importTarget: "@holo-js/adapter-nuxt/realtime", filePath });
|
|
12
13
|
}
|
|
13
|
-
function createRealtimeClientDefinitionTransform(source) {
|
|
14
|
-
return createRealtimeClientDefinitionTransform$1(source, "@holo-js/adapter-nuxt/realtime");
|
|
14
|
+
function createRealtimeClientDefinitionTransform(source, filePath) {
|
|
15
|
+
return createRealtimeClientDefinitionTransform$1({ source, importTarget: "@holo-js/adapter-nuxt/realtime", filePath });
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
const MODEL_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
18
19
|
const HOLO_PACKAGE_SCOPE = "@holo-js/";
|
|
19
20
|
const realtimeClientDefinitionPrefix = "\0holo-realtime-client:";
|
|
20
|
-
const
|
|
21
|
+
const realtimeDefinitionsModuleId = "#holo/realtime-definitions";
|
|
22
|
+
const VALIDATION_CLIENT_DEPENDENCIES = ["valibot", "@holo-js/kernel > file-type"];
|
|
21
23
|
const CLIENT_OPTIMIZE_DEPS = [
|
|
22
24
|
{
|
|
23
25
|
packageName: `${HOLO_PACKAGE_SCOPE}forms`,
|
|
@@ -230,10 +232,10 @@ function addViteOptimizeDeps(opts, deps) {
|
|
|
230
232
|
])
|
|
231
233
|
];
|
|
232
234
|
}
|
|
233
|
-
function isRealtimeDefinitionModule(
|
|
235
|
+
function isRealtimeDefinitionModule(rootDir, id, directories = resolveRealtimeDefinitionDirectories(rootDir)) {
|
|
234
236
|
const [sourcePath = ""] = id.split("?");
|
|
235
237
|
const normalizedId = sourcePath.replaceAll("\\", "/");
|
|
236
|
-
return normalizedId.includes("/server/realtime/") && MODEL_FILE_EXTENSIONS.has(extname(normalizedId));
|
|
238
|
+
return (normalizedId.includes("/server/realtime/") || isInRealtimeDefinitionDirectory(directories, normalizedId)) && MODEL_FILE_EXTENSIONS.has(extname(normalizedId));
|
|
237
239
|
}
|
|
238
240
|
function resolveExistingRealtimeDefinitionFile(path) {
|
|
239
241
|
if (MODEL_FILE_EXTENSIONS.has(extname(path)) && existsFile(path)) {
|
|
@@ -261,10 +263,39 @@ function resolveRealtimeDefinitionImport(source, importer) {
|
|
|
261
263
|
const basePath = source.startsWith(".") ? importer ? resolve(dirname(importer.split("?")[0]), source) : void 0 : resolve(source);
|
|
262
264
|
return basePath ? resolveExistingRealtimeDefinitionFile(basePath) : void 0;
|
|
263
265
|
}
|
|
266
|
+
function collectRealtimeDefinitionFiles(rootDir) {
|
|
267
|
+
const realtimeRoot = resolve(rootDir, "server/realtime");
|
|
268
|
+
if (!existsSync(realtimeRoot)) {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
return readdirSync(realtimeRoot, { recursive: true, withFileTypes: true }).filter((entry) => entry.isFile() && MODEL_FILE_EXTENSIONS.has(extname(entry.name))).map((entry) => resolve(entry.parentPath, entry.name)).sort((left, right) => left.localeCompare(right));
|
|
272
|
+
}
|
|
273
|
+
function renderRealtimeDefinitionsModule(rootDir) {
|
|
274
|
+
const files = collectRealtimeDefinitionFiles(rootDir);
|
|
275
|
+
const imports = files.map((filePath, index) => `import * as realtimeModule${index} from ${JSON.stringify(filePath)}`);
|
|
276
|
+
const values = files.map((_filePath, index) => `...Object.values(realtimeModule${index})`);
|
|
277
|
+
return [
|
|
278
|
+
...imports,
|
|
279
|
+
"",
|
|
280
|
+
`export const realtimeDefinitions = [${values.join(", ")}]`,
|
|
281
|
+
""
|
|
282
|
+
].join("\n");
|
|
283
|
+
}
|
|
264
284
|
function createRealtimeDefinitionVitePlugin(rootDir) {
|
|
285
|
+
const directories = resolveRealtimeDefinitionDirectories(rootDir);
|
|
286
|
+
let realtimeTreeChanged = false;
|
|
287
|
+
const loadedDefinitionIds = /* @__PURE__ */ new Set();
|
|
265
288
|
return {
|
|
266
289
|
name: "holo-realtime-client-definitions",
|
|
267
290
|
enforce: "pre",
|
|
291
|
+
buildStart() {
|
|
292
|
+
assertRealtimeDefinitionTree(rootDir);
|
|
293
|
+
},
|
|
294
|
+
configureServer(server) {
|
|
295
|
+
server.watcher.on("all", (_event, path) => {
|
|
296
|
+
if (isInRealtimeDefinitionDirectory(directories, path)) realtimeTreeChanged = true;
|
|
297
|
+
});
|
|
298
|
+
},
|
|
268
299
|
resolveId(source, importer, options) {
|
|
269
300
|
if (options?.ssr) {
|
|
270
301
|
return null;
|
|
@@ -272,22 +303,32 @@ function createRealtimeDefinitionVitePlugin(rootDir) {
|
|
|
272
303
|
const resolved = resolveRealtimeDefinitionImport(source, importer);
|
|
273
304
|
return resolved ? `${realtimeClientDefinitionPrefix}${resolved}` : null;
|
|
274
305
|
},
|
|
275
|
-
load(id) {
|
|
276
|
-
if (!
|
|
306
|
+
load(id, options) {
|
|
307
|
+
if (!options?.ssr && realtimeTreeChanged) {
|
|
308
|
+
assertRealtimeDefinitionTree(rootDir);
|
|
309
|
+
realtimeTreeChanged = false;
|
|
310
|
+
}
|
|
311
|
+
if (id.startsWith(realtimeClientDefinitionPrefix)) {
|
|
312
|
+
const sourcePath2 = id.slice(realtimeClientDefinitionPrefix.length);
|
|
313
|
+
loadedDefinitionIds.add(id);
|
|
314
|
+
return createRealtimeClientDefinitionModule(readFileSync(sourcePath2, "utf8"), sourcePath2);
|
|
315
|
+
}
|
|
316
|
+
if (options?.ssr || !isRealtimeDefinitionModule(rootDir, id, directories)) {
|
|
277
317
|
return null;
|
|
278
318
|
}
|
|
279
|
-
const sourcePath = id.
|
|
280
|
-
|
|
319
|
+
const sourcePath = id.split("?")[0];
|
|
320
|
+
loadedDefinitionIds.add(id);
|
|
321
|
+
return createRealtimeClientDefinitionModule(readFileSync(sourcePath, "utf8"), sourcePath);
|
|
281
322
|
},
|
|
282
323
|
transform(code, id, options) {
|
|
283
324
|
if (!options?.ssr && !id.includes("node_modules") && /\.[cm]?[jt]sx?$/.test(id)) {
|
|
284
325
|
const compiled = compileBrowserValidation(code, id);
|
|
285
326
|
if (compiled) return { code: compiled, map: null };
|
|
286
327
|
}
|
|
287
|
-
if (options?.ssr || !isRealtimeDefinitionModule(rootDir, id)) {
|
|
328
|
+
if (options?.ssr || loadedDefinitionIds.has(id) || !isRealtimeDefinitionModule(rootDir, id, directories)) {
|
|
288
329
|
return null;
|
|
289
330
|
}
|
|
290
|
-
return createRealtimeClientDefinitionTransform(code);
|
|
331
|
+
return createRealtimeClientDefinitionTransform(code, id.split("?")[0]);
|
|
291
332
|
}
|
|
292
333
|
};
|
|
293
334
|
}
|
|
@@ -328,20 +369,28 @@ function hasLoadedConfigFile(loaded, configName) {
|
|
|
328
369
|
return normalizedPath.endsWith(`/config/${configName}.ts`) || normalizedPath.endsWith(`/config/${configName}.mts`) || normalizedPath.endsWith(`/config/${configName}.js`) || normalizedPath.endsWith(`/config/${configName}.mjs`) || normalizedPath.endsWith(`/config/${configName}.cts`) || normalizedPath.endsWith(`/config/${configName}.cjs`);
|
|
329
370
|
});
|
|
330
371
|
}
|
|
372
|
+
async function writeGeneratedFile(path, contents) {
|
|
373
|
+
const { atomicFileInternals } = await import('@holo-js/core');
|
|
374
|
+
await atomicFileInternals.writeTextFile(path, contents);
|
|
375
|
+
}
|
|
331
376
|
async function ensureModelRegistryTypesPlaceholder(path) {
|
|
332
377
|
try {
|
|
333
378
|
await access(path);
|
|
334
379
|
return;
|
|
335
380
|
} catch {
|
|
336
381
|
await mkdir(dirname(path), { recursive: true });
|
|
337
|
-
await
|
|
382
|
+
await writeGeneratedFile(path, "// Generated by holo prepare. Do not edit.\n\nexport {}\n");
|
|
338
383
|
}
|
|
339
384
|
}
|
|
385
|
+
const STORAGE_CREDENTIAL_KEYS = /* @__PURE__ */ new Set(["accessKeyId", "secretAccessKey", "sessionToken"]);
|
|
386
|
+
function withoutStorageCredentials(disk) {
|
|
387
|
+
return Object.fromEntries(Object.entries(disk).filter(([key]) => !STORAGE_CREDENTIAL_KEYS.has(key)));
|
|
388
|
+
}
|
|
340
389
|
function toStorageModuleOptions(loaded) {
|
|
341
390
|
return {
|
|
342
391
|
defaultDisk: loaded.storage.defaultDisk,
|
|
343
392
|
routePrefix: loaded.storage.routePrefix,
|
|
344
|
-
disks:
|
|
393
|
+
disks: Object.fromEntries(Object.entries(loaded.storage.disks).map(([name, disk]) => [name, withoutStorageCredentials(disk)]))
|
|
345
394
|
};
|
|
346
395
|
}
|
|
347
396
|
function resolveStorageSetup(storageModule, loaded) {
|
|
@@ -357,11 +406,8 @@ function resolveStorageSetup(storageModule, loaded) {
|
|
|
357
406
|
normalized: storageModule.normalizeModuleOptions(options)
|
|
358
407
|
};
|
|
359
408
|
}
|
|
360
|
-
function finalizeStorageSetup(storageModule, opts, resolver
|
|
409
|
+
function finalizeStorageSetup(storageModule, opts, resolver) {
|
|
361
410
|
const normalized = storageModule.normalizeModuleOptions(opts._holoStorageModuleOptions);
|
|
362
|
-
opts.runtimeConfig = opts.runtimeConfig || {};
|
|
363
|
-
opts.runtimeConfig.holoStorage = normalized;
|
|
364
|
-
storageModule.applyNitroStorageConfig(opts, normalized, s3Driver);
|
|
365
411
|
if (storageModule.hasPublicLocalDisk(normalized)) {
|
|
366
412
|
addServerHandler({
|
|
367
413
|
route: `${normalized.routePrefix}/**`,
|
|
@@ -369,11 +415,6 @@ function finalizeStorageSetup(storageModule, opts, resolver, s3Driver) {
|
|
|
369
415
|
});
|
|
370
416
|
}
|
|
371
417
|
}
|
|
372
|
-
function applyStorageRuntimeConfig(runtimeConfig, normalized) {
|
|
373
|
-
if (normalized) {
|
|
374
|
-
runtimeConfig.holoStorage = normalized;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
418
|
async function createServerModelImports(projectRoot, modelsRelativePath, generatedSchemaRelativePath) {
|
|
378
419
|
const modelsDir = resolve(projectRoot, modelsRelativePath);
|
|
379
420
|
const generatedSchemaPath = resolve(projectRoot, generatedSchemaRelativePath);
|
|
@@ -409,16 +450,16 @@ async function createServerModelImports(projectRoot, modelsRelativePath, generat
|
|
|
409
450
|
"export default () => {}"
|
|
410
451
|
];
|
|
411
452
|
await mkdir(modelImportDir, { recursive: true });
|
|
412
|
-
await
|
|
413
|
-
|
|
414
|
-
await
|
|
415
|
-
|
|
453
|
+
await writeGeneratedFile(modelImportFile, `${lines.join("\n")}
|
|
454
|
+
`);
|
|
455
|
+
await writeGeneratedFile(modelPluginFile, `${pluginLines.join("\n")}
|
|
456
|
+
`);
|
|
416
457
|
return {
|
|
417
458
|
importDir: modelImportDir,
|
|
418
459
|
pluginFile: modelPluginFile
|
|
419
460
|
};
|
|
420
461
|
}
|
|
421
|
-
const
|
|
462
|
+
const holoNuxtModule = defineNuxtModule({
|
|
422
463
|
meta: {
|
|
423
464
|
name: "@holo-js/adapter-nuxt"
|
|
424
465
|
},
|
|
@@ -434,7 +475,6 @@ const module$1 = defineNuxtModule({
|
|
|
434
475
|
addViteOptimizeDeps(opts, resolveClientOptimizeDeps(rootDir));
|
|
435
476
|
const storageModule = await importOptionalStorageModule();
|
|
436
477
|
const loaded = await loadConfigDirectory(rootDir, {
|
|
437
|
-
preferCache: process.env.NODE_ENV === "production",
|
|
438
478
|
processEnv: process.env
|
|
439
479
|
});
|
|
440
480
|
if ((loaded.app.plugins ?? []).includes("@holo-js/panels")) {
|
|
@@ -444,9 +484,7 @@ const module$1 = defineNuxtModule({
|
|
|
444
484
|
}
|
|
445
485
|
}
|
|
446
486
|
const storageSetup = resolveStorageSetup(storageModule, loaded);
|
|
447
|
-
|
|
448
|
-
opts.nitro = opts.nitro || { storage: {} };
|
|
449
|
-
opts.nitro.storage = opts.nitro.storage || {};
|
|
487
|
+
opts.nitro = opts.nitro || {};
|
|
450
488
|
if (isProjectPackageLinked(rootDir, "@holo-js/core")) {
|
|
451
489
|
opts.nitro.rollupConfig = {
|
|
452
490
|
...opts.nitro.rollupConfig,
|
|
@@ -467,11 +505,8 @@ const module$1 = defineNuxtModule({
|
|
|
467
505
|
};
|
|
468
506
|
opts.runtimeConfig.holo = {
|
|
469
507
|
appUrl: loaded.app.url,
|
|
470
|
-
appEnv: loaded.app.env,
|
|
471
|
-
appDebug: loaded.app.debug,
|
|
472
508
|
projectRoot: rootDir
|
|
473
509
|
};
|
|
474
|
-
opts.runtimeConfig.db = loaded.database;
|
|
475
510
|
const mergedStorageOptions = storageSetup.options;
|
|
476
511
|
const normalizedStorage = storageSetup.normalized;
|
|
477
512
|
opts._holoStorageModuleOptions = mergedStorageOptions;
|
|
@@ -480,11 +515,10 @@ const module$1 = defineNuxtModule({
|
|
|
480
515
|
throw new Error("[@holo-js/adapter-nuxt] S3 storage disks require @holo-js/storage-s3 to be installed.");
|
|
481
516
|
}
|
|
482
517
|
}
|
|
483
|
-
applyStorageRuntimeConfig(opts.runtimeConfig, normalizedStorage);
|
|
484
518
|
if (!opts._holoCoreRuntimeRegistered) {
|
|
485
519
|
if (existsSync(resolve(rootDir, ".holo-js/generated/definition-importer.ts"))) {
|
|
486
520
|
const initPluginPath = resolve(rootDir, ".holo-js/generated/nuxt-init.ts");
|
|
487
|
-
await
|
|
521
|
+
await writeGeneratedFile(initPluginPath, [
|
|
488
522
|
`import init from ${JSON.stringify(resolver.resolve("./runtime/plugins/init"))}`,
|
|
489
523
|
"import { installProjectDefinitionImporter } from './definition-importer'",
|
|
490
524
|
"",
|
|
@@ -492,7 +526,7 @@ const module$1 = defineNuxtModule({
|
|
|
492
526
|
"",
|
|
493
527
|
"export default init",
|
|
494
528
|
""
|
|
495
|
-
].join("\n")
|
|
529
|
+
].join("\n"));
|
|
496
530
|
addServerPlugin(initPluginPath);
|
|
497
531
|
} else {
|
|
498
532
|
addServerPlugin(resolver.resolve("./runtime/plugins/init"));
|
|
@@ -511,10 +545,6 @@ const module$1 = defineNuxtModule({
|
|
|
511
545
|
}
|
|
512
546
|
opts._holoCoreRuntimeRegistered = true;
|
|
513
547
|
}
|
|
514
|
-
if (storageModule && !opts._holoStorageRuntimeRegistered) {
|
|
515
|
-
addServerPlugin(resolver.resolve("./runtime/plugins/storage"));
|
|
516
|
-
opts._holoStorageRuntimeRegistered = true;
|
|
517
|
-
}
|
|
518
548
|
if (hasProjectPackage(rootDir, "@holo-js/broadcast") && !opts._holoBroadcastAuthRouteRegistered) {
|
|
519
549
|
addServerHandler({
|
|
520
550
|
route: "/broadcasting/config",
|
|
@@ -551,11 +581,12 @@ const module$1 = defineNuxtModule({
|
|
|
551
581
|
route: "/holo/realtime/mutation",
|
|
552
582
|
handler: resolver.resolve("./runtime/server/routes/realtime-mutation.post")
|
|
553
583
|
});
|
|
584
|
+
opts.nitro.virtual = {
|
|
585
|
+
...opts.nitro.virtual,
|
|
586
|
+
[realtimeDefinitionsModuleId]: () => renderRealtimeDefinitionsModule(rootDir)
|
|
587
|
+
};
|
|
554
588
|
opts._holoRealtimeRouteRegistered = true;
|
|
555
589
|
}
|
|
556
|
-
if (storageModule && normalizedStorage && (!opts.nitro.storage || Object.keys(opts.nitro.storage).every((key) => !key.startsWith("holo:")))) {
|
|
557
|
-
storageModule.applyNitroStorageConfig(opts, normalizedStorage, s3Driver);
|
|
558
|
-
}
|
|
559
590
|
const runtimePath = resolver.resolve("./runtime");
|
|
560
591
|
for (const packageName of [runtimePath, "@holo-js/auth"]) {
|
|
561
592
|
if (!opts.build.transpile.includes(packageName)) {
|
|
@@ -565,7 +596,7 @@ const module$1 = defineNuxtModule({
|
|
|
565
596
|
if (storageModule && !opts._holoStorageFinalizeRegistered) {
|
|
566
597
|
opts._holoStorageFinalizeRegistered = true;
|
|
567
598
|
nuxt.hook("modules:done", () => {
|
|
568
|
-
finalizeStorageSetup(storageModule, opts, resolver
|
|
599
|
+
finalizeStorageSetup(storageModule, opts, resolver);
|
|
569
600
|
});
|
|
570
601
|
}
|
|
571
602
|
if (!opts._holoTypesRegistered) {
|
|
@@ -573,12 +604,27 @@ const module$1 = defineNuxtModule({
|
|
|
573
604
|
await ensureModelRegistryTypesPlaceholder(authTypesPath);
|
|
574
605
|
await ensureModelRegistryTypesPlaceholder(authorizationTypesPath);
|
|
575
606
|
await ensureModelRegistryTypesPlaceholder(modelRegistryTypesPath);
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
references.push(
|
|
580
|
-
|
|
581
|
-
|
|
607
|
+
const serverTypeReferences = [authTypesPath, authorizationTypesPath, modelRegistryTypesPath, ...pluginTypesPaths].map((path) => ({ path }));
|
|
608
|
+
const serverTypeIncludes = [resolve(rootDir, "config/**/*")];
|
|
609
|
+
nuxt.hook("nitro:prepare:types", ({ references }) => {
|
|
610
|
+
references.push(...serverTypeReferences);
|
|
611
|
+
});
|
|
612
|
+
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
613
|
+
nitroConfig.typescript ??= {};
|
|
614
|
+
nitroConfig.typescript.tsConfig ??= {};
|
|
615
|
+
nitroConfig.typescript.tsConfig.include = [...nitroConfig.typescript.tsConfig.include ?? [], ...serverTypeIncludes];
|
|
616
|
+
});
|
|
617
|
+
nuxt.hook("prepare:types", async ({ references, serverReferences, tsConfig, serverTsConfig }) => {
|
|
618
|
+
references.push({ types: "@holo-js/adapter-nuxt" }, ...serverTypeReferences);
|
|
619
|
+
serverReferences?.push(...serverTypeReferences);
|
|
620
|
+
if (serverTsConfig) serverTsConfig.include = [...serverTsConfig.include ?? [], ...serverTypeIncludes];
|
|
621
|
+
const options = nuxt.options;
|
|
622
|
+
const { projectTsconfigInternals } = await import('@holo-js/core');
|
|
623
|
+
await projectTsconfigInternals.writeNuxtTsconfigSnapshot(
|
|
624
|
+
rootDir,
|
|
625
|
+
options.typesDir || options.buildDir || resolve(rootDir, ".nuxt"),
|
|
626
|
+
{ tsConfig, serverTsConfig }
|
|
627
|
+
);
|
|
582
628
|
});
|
|
583
629
|
}
|
|
584
630
|
}
|
|
@@ -594,7 +640,6 @@ const moduleInternals = {
|
|
|
594
640
|
hasModuleNotFoundCode,
|
|
595
641
|
hasLoadedConfigFile,
|
|
596
642
|
importOptionalStorageS3Module,
|
|
597
|
-
applyStorageRuntimeConfig,
|
|
598
643
|
finalizeStorageSetup,
|
|
599
644
|
resolveStorageSetup,
|
|
600
645
|
isModuleResolutionFailure,
|
|
@@ -603,6 +648,7 @@ const moduleInternals = {
|
|
|
603
648
|
isProjectPackageLinked,
|
|
604
649
|
isRealtimeDefinitionModule,
|
|
605
650
|
resolveClientOptimizeDeps,
|
|
651
|
+
renderRealtimeDefinitionsModule,
|
|
606
652
|
resolveExistingRealtimeDefinitionFile,
|
|
607
653
|
resolveRealtimeDefinitionImport
|
|
608
654
|
};
|
|
@@ -610,4 +656,4 @@ const adapterNuxtInternals = {
|
|
|
610
656
|
toStorageModuleOptions
|
|
611
657
|
};
|
|
612
658
|
|
|
613
|
-
export { adapterNuxtInternals,
|
|
659
|
+
export { adapterNuxtInternals, holoNuxtModule as default, moduleInternals };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { HoloAdapterProjectAccessors } from '@holo-js/core'
|
|
2
|
-
import type { RuntimeDatabaseConfig } from '@holo-js/db'
|
|
3
2
|
|
|
4
3
|
export interface HoloRuntimeConnection {
|
|
5
4
|
driver?: 'sqlite' | 'postgres' | 'mysql'
|
|
@@ -29,12 +28,9 @@ export interface HoloRuntimeDefaultConnection extends HoloRuntimeConnection {
|
|
|
29
28
|
export declare const holo: HoloAdapterProjectAccessors
|
|
30
29
|
export declare function configureHoloRuntimeConfig(config: {
|
|
31
30
|
holo: {
|
|
32
|
-
appEnv: 'production' | 'development' | 'test'
|
|
33
|
-
appDebug: boolean
|
|
34
31
|
appUrl?: string
|
|
35
32
|
projectRoot?: string
|
|
36
33
|
}
|
|
37
|
-
db?: RuntimeDatabaseConfig
|
|
38
34
|
}): void
|
|
39
35
|
export declare function resetHoloRuntimeConfig(): void
|
|
40
36
|
export type NuxtAuthRequestEvent = {
|
|
@@ -52,8 +48,10 @@ export type NuxtAuthRequestEvent = {
|
|
|
52
48
|
readonly headers?: Readonly<Record<string, string | readonly string[] | undefined>>
|
|
53
49
|
}
|
|
54
50
|
readonly res?: {
|
|
51
|
+
statusCode: number
|
|
55
52
|
getHeader(name: string): number | string | readonly string[] | undefined
|
|
56
53
|
setHeader(name: string, value: number | string | readonly string[]): void
|
|
54
|
+
end(): void
|
|
57
55
|
}
|
|
58
56
|
}
|
|
59
57
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { resolveProjectAppEnvironmentSync } from "@holo-js/config";
|
|
1
2
|
import {
|
|
2
3
|
createHoloProjectAccessors,
|
|
4
|
+
getHolo,
|
|
3
5
|
initializeHoloAdapterProject
|
|
4
6
|
} from "@holo-js/core";
|
|
5
|
-
import {
|
|
7
|
+
import { createCookieWriteUnavailableError } from "@holo-js/kernel";
|
|
6
8
|
export function configureHoloRuntimeConfig(config) {
|
|
7
9
|
const runtimeGlobals = globalThis;
|
|
8
10
|
runtimeGlobals.__holoRuntimeConfig = config;
|
|
@@ -21,14 +23,12 @@ function getRuntimeConfig() {
|
|
|
21
23
|
}
|
|
22
24
|
return runtimeGlobals.useRuntimeConfig();
|
|
23
25
|
}
|
|
24
|
-
function resolveRuntimeEnvName(env) {
|
|
25
|
-
return env;
|
|
26
|
-
}
|
|
27
26
|
function resolveRuntimeProjectRoot(config) {
|
|
28
27
|
return config.holo.projectRoot?.trim() || process.cwd();
|
|
29
28
|
}
|
|
30
|
-
async function
|
|
31
|
-
|
|
29
|
+
async function resolveNitroEvent() {
|
|
30
|
+
const { useEvent } = await import("#imports");
|
|
31
|
+
return useEvent();
|
|
32
32
|
}
|
|
33
33
|
export function createNuxtAuthRequestAccessors(requestEvent) {
|
|
34
34
|
function safeDecode(value) {
|
|
@@ -39,7 +39,7 @@ export function createNuxtAuthRequestAccessors(requestEvent) {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
async function readHeader(name) {
|
|
42
|
-
const event = requestEvent ??
|
|
42
|
+
const event = requestEvent ?? await resolveNitroEvent();
|
|
43
43
|
if (!event) {
|
|
44
44
|
return void 0;
|
|
45
45
|
}
|
|
@@ -80,11 +80,14 @@ export function createNuxtAuthRequestAccessors(requestEvent) {
|
|
|
80
80
|
}
|
|
81
81
|
return void 0;
|
|
82
82
|
}
|
|
83
|
+
async function resolveResponse() {
|
|
84
|
+
const event = requestEvent ?? await resolveNitroEvent();
|
|
85
|
+
return event?.node?.res;
|
|
86
|
+
}
|
|
83
87
|
async function appendCookie(cookie) {
|
|
84
|
-
const
|
|
85
|
-
const response = event?.node?.res;
|
|
88
|
+
const response = await resolveResponse();
|
|
86
89
|
if (!response) {
|
|
87
|
-
|
|
90
|
+
throw createCookieWriteUnavailableError("Authentication cookies cannot be set outside an active Nitro request.");
|
|
88
91
|
}
|
|
89
92
|
const current = response.getHeader("set-cookie");
|
|
90
93
|
if (Array.isArray(current)) {
|
|
@@ -103,20 +106,26 @@ export function createNuxtAuthRequestAccessors(requestEvent) {
|
|
|
103
106
|
const getHeaderValue = async (name) => {
|
|
104
107
|
return await readHeader(name);
|
|
105
108
|
};
|
|
109
|
+
const canAppendResponseCookie = async () => {
|
|
110
|
+
return Boolean(await resolveResponse());
|
|
111
|
+
};
|
|
106
112
|
const appendResponseCookie = async (cookie) => {
|
|
107
113
|
await appendCookie(cookie);
|
|
108
114
|
};
|
|
109
115
|
const redirectResponse = async (url, status) => {
|
|
110
|
-
const event = requestEvent ??
|
|
111
|
-
|
|
116
|
+
const event = requestEvent ?? await resolveNitroEvent();
|
|
117
|
+
const response = event?.node?.res;
|
|
118
|
+
if (!response) {
|
|
112
119
|
throw new TypeError("Holo Nuxt auth redirect requires an active Nitro event.");
|
|
113
120
|
}
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
response.statusCode = status ?? 302;
|
|
122
|
+
response.setHeader("location", url);
|
|
123
|
+
response.end();
|
|
116
124
|
};
|
|
117
125
|
return {
|
|
118
126
|
getCookie: getCookieValue,
|
|
119
127
|
getHeader: getHeaderValue,
|
|
128
|
+
canAppendResponseCookie,
|
|
120
129
|
appendResponseCookie,
|
|
121
130
|
redirectResponse
|
|
122
131
|
};
|
|
@@ -130,9 +139,8 @@ export async function runWithNuxtRequest(event, callback) {
|
|
|
130
139
|
export const holo = createHoloProjectAccessors(async () => {
|
|
131
140
|
const config = getRuntimeConfig();
|
|
132
141
|
const authRequest = createNuxtAuthRequestAccessors();
|
|
142
|
+
const { createNuxtAuthorizationError } = await import("../authorization-error.js");
|
|
133
143
|
const project = await initializeHoloAdapterProject(resolveRuntimeProjectRoot(config), {
|
|
134
|
-
envName: resolveRuntimeEnvName(config.holo.appEnv),
|
|
135
|
-
preferCache: process.env.NODE_ENV === "production",
|
|
136
144
|
processEnv: process.env,
|
|
137
145
|
authRequest,
|
|
138
146
|
authorizationError: {
|
|
@@ -142,17 +150,12 @@ export const holo = createHoloProjectAccessors(async () => {
|
|
|
142
150
|
const runtimeGlobals = globalThis;
|
|
143
151
|
runtimeGlobals.__holoNuxtAuthRequestRunner = (accessors, callback) => project.runtime.runWithAuthRequestAccessors(accessors, callback);
|
|
144
152
|
return project;
|
|
145
|
-
}, {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (connectionNames.includes("default")) {
|
|
152
|
-
return "default";
|
|
153
|
-
}
|
|
154
|
-
return connectionNames[0] ?? "default";
|
|
155
|
-
}
|
|
153
|
+
}, {
|
|
154
|
+
cache: () => resolveProjectAppEnvironmentSync({
|
|
155
|
+
cwd: resolveRuntimeProjectRoot(getRuntimeConfig()),
|
|
156
|
+
processEnv: process.env
|
|
157
|
+
}) === "production"
|
|
158
|
+
});
|
|
156
159
|
function normalizeConnection(connection) {
|
|
157
160
|
const driver = connection.driver ?? (connection.filename ? "sqlite" : void 0);
|
|
158
161
|
const database = connection.database ?? connection.filename;
|
|
@@ -171,14 +174,10 @@ function normalizeConnection(connection) {
|
|
|
171
174
|
};
|
|
172
175
|
}
|
|
173
176
|
export function useHoloDb() {
|
|
174
|
-
const
|
|
175
|
-
const group = config.db ?? { connections: {} };
|
|
177
|
+
const group = getHolo().loadedConfig.database;
|
|
176
178
|
const connections = group.connections ?? {};
|
|
177
179
|
return {
|
|
178
|
-
defaultConnection:
|
|
179
|
-
defaultConnection: group.defaultConnection,
|
|
180
|
-
connections
|
|
181
|
-
}),
|
|
180
|
+
defaultConnection: group.defaultConnection ?? "default",
|
|
182
181
|
connections: Object.fromEntries(
|
|
183
182
|
Object.entries(connections).map(([name, connection]) => {
|
|
184
183
|
if (typeof connection === "string") {
|
|
@@ -190,10 +189,8 @@ export function useHoloDb() {
|
|
|
190
189
|
};
|
|
191
190
|
}
|
|
192
191
|
export function useHoloEnv() {
|
|
193
|
-
|
|
194
|
-
return resolveRuntimeEnvName(config.holo.appEnv);
|
|
192
|
+
return getHolo().loadedConfig.environment.name;
|
|
195
193
|
}
|
|
196
194
|
export function useHoloDebug() {
|
|
197
|
-
|
|
198
|
-
return config.holo.appDebug;
|
|
195
|
+
return getHolo().loadedConfig.app.debug;
|
|
199
196
|
}
|
|
@@ -3,31 +3,47 @@ import {
|
|
|
3
3
|
configureHoloRuntimeConfig
|
|
4
4
|
} from "../composables/index.js";
|
|
5
5
|
import { createNuxtAuthorizationError } from "../authorization-error.js";
|
|
6
|
+
import { connectionAddress } from "../server/connection-address.js";
|
|
6
7
|
import { initializeHoloAdapterProject } from "@holo-js/core";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
8
|
+
import { requestUrlInternals } from "@holo-js/kernel";
|
|
9
|
+
import { captureErrorReporter } from "@holo-js/kernel/error-reporting";
|
|
10
|
+
import { defineNitroPlugin, useEvent, useRuntimeConfig } from "#imports";
|
|
9
11
|
export default defineNitroPlugin((nitroApp) => {
|
|
10
12
|
const config = useRuntimeConfig();
|
|
11
13
|
configureHoloRuntimeConfig(config);
|
|
14
|
+
requestUrlInternals.provideConnectionAddress(() => connectionAddress(useEvent().node.req.socket));
|
|
12
15
|
const project = initializeHoloAdapterProject(config.holo.projectRoot?.trim() || process.cwd(), {
|
|
13
|
-
envName: config.holo.appEnv,
|
|
14
|
-
preferCache: process.env.NODE_ENV === "production",
|
|
15
16
|
processEnv: process.env,
|
|
16
17
|
authRequest: createNuxtAuthRequestAccessors(),
|
|
17
18
|
authorizationError: {
|
|
18
19
|
createError: createNuxtAuthorizationError
|
|
19
20
|
}
|
|
20
21
|
});
|
|
22
|
+
let projectPending = true;
|
|
23
|
+
const markProjectSettled = () => {
|
|
24
|
+
projectPending = false;
|
|
25
|
+
};
|
|
26
|
+
const projectSettled = project.then(markProjectSettled, markProjectSettled);
|
|
21
27
|
const onRequest = nitroApp.h3App.options.onRequest;
|
|
22
28
|
nitroApp.h3App.options.onRequest = async (event) => {
|
|
23
29
|
await project;
|
|
24
30
|
return await onRequest?.(event);
|
|
25
31
|
};
|
|
26
|
-
|
|
32
|
+
const shutdownProject = async () => {
|
|
33
|
+
let report;
|
|
27
34
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
const { runtime } = await project;
|
|
36
|
+
report = captureErrorReporter();
|
|
37
|
+
await runtime.shutdown();
|
|
38
|
+
} catch (error) {
|
|
39
|
+
(report ?? captureErrorReporter())(error, { capability: "nuxt", operation: "shutdown" });
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
nitroApp.hooks.hook("close", async () => {
|
|
43
|
+
if (projectPending) {
|
|
44
|
+
void projectSettled.then(shutdownProject);
|
|
30
45
|
return;
|
|
31
46
|
}
|
|
47
|
+
await shutdownProject();
|
|
32
48
|
});
|
|
33
49
|
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
function isUnixSocket(socket) {
|
|
2
|
+
return !socket.destroyed && Object.keys(socket.address()).length === 0;
|
|
3
|
+
}
|
|
4
|
+
export function connectionAddress(socket) {
|
|
5
|
+
if (socket.remoteAddress) return socket.remoteAddress;
|
|
6
|
+
return isUnixSocket(socket) ? "127.0.0.1" : void 0;
|
|
7
|
+
}
|
|
@@ -1,26 +1,17 @@
|
|
|
1
|
-
import { send, setHeader, setResponseStatus } from "
|
|
2
|
-
import {
|
|
3
|
-
import { serializeRateLimitError } from "@holo-js/adapter-shared/server-errors";
|
|
1
|
+
import { send, setHeader, setResponseStatus } from "#imports";
|
|
2
|
+
import { serializeValidationException } from "@holo-js/validation";
|
|
3
|
+
import { serializeRateLimitError, serializeRequestBodyTooLargeError } from "@holo-js/adapter-shared/server-errors";
|
|
4
4
|
import {
|
|
5
5
|
applyFormFailureRedirect,
|
|
6
6
|
shouldRedirectFormFailure
|
|
7
7
|
} from "./form-failure.js";
|
|
8
|
-
function findValidationException(error) {
|
|
9
|
-
if (isValidationException(error)) {
|
|
10
|
-
return error;
|
|
11
|
-
}
|
|
12
|
-
if (!error || typeof error !== "object" || !("cause" in error)) {
|
|
13
|
-
return void 0;
|
|
14
|
-
}
|
|
15
|
-
return findValidationException(error.cause);
|
|
16
|
-
}
|
|
17
8
|
export default async function handleValidationError(error, event) {
|
|
18
|
-
const
|
|
19
|
-
const payload =
|
|
9
|
+
const bodyTooLarge = serializeRequestBodyTooLargeError(error);
|
|
10
|
+
const payload = bodyTooLarge ?? serializeValidationException(error) ?? serializeRateLimitError(error);
|
|
20
11
|
if (!payload) {
|
|
21
12
|
return;
|
|
22
13
|
}
|
|
23
|
-
if (shouldRedirectFormFailure(event)) {
|
|
14
|
+
if (!bodyTooLarge && shouldRedirectFormFailure(event)) {
|
|
24
15
|
const failure = { ...payload };
|
|
25
16
|
const response = {};
|
|
26
17
|
applyFormFailureRedirect(event, response, failure);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveRequestUrl, sameOriginRedirectPath } from "@holo-js/kernel";
|
|
1
2
|
const FORM_FAILURE_COOKIE = "holo_form_failure";
|
|
2
3
|
function getHeader(event, name) {
|
|
3
4
|
const value = event.node?.req?.headers?.[name.toLowerCase()];
|
|
@@ -42,12 +43,12 @@ function getRedirectLocation(event) {
|
|
|
42
43
|
try {
|
|
43
44
|
const url = new URL(referer);
|
|
44
45
|
if (url.host === host) {
|
|
45
|
-
return `${url.pathname}${url.search}
|
|
46
|
+
return sameOriginRedirectPath(`${url.pathname}${url.search}`);
|
|
46
47
|
}
|
|
47
48
|
} catch {
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
|
-
return event.path ?? event.node?.req?.url
|
|
51
|
+
return sameOriginRedirectPath(event.path ?? event.node?.req?.url);
|
|
51
52
|
}
|
|
52
53
|
function appendSetCookie(event, response, cookie) {
|
|
53
54
|
const responseCookie = event.node?.res?.getHeader?.("set-cookie");
|
|
@@ -79,9 +80,21 @@ function setHeader(event, response, name, value) {
|
|
|
79
80
|
response.headers ??= {};
|
|
80
81
|
response.headers[name] = value;
|
|
81
82
|
}
|
|
82
|
-
function
|
|
83
|
-
const
|
|
84
|
-
|
|
83
|
+
function isSecureRequest(event) {
|
|
84
|
+
const request = event.node?.req;
|
|
85
|
+
const socket = request?.socket;
|
|
86
|
+
const protocol = socket && "encrypted" in socket && socket.encrypted === true ? "https" : "http";
|
|
87
|
+
try {
|
|
88
|
+
return resolveRequestUrl({ url: `${protocol}://localhost/`, headers: request?.headers }).protocol === "https:";
|
|
89
|
+
} catch {
|
|
90
|
+
return protocol === "https";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function serializeFormFailureCookie(event, payload) {
|
|
94
|
+
const { values: _values, ...failure } = payload;
|
|
95
|
+
const encoded = encodeURIComponent(JSON.stringify(failure));
|
|
96
|
+
const secure = isSecureRequest(event) ? "; Secure" : "";
|
|
97
|
+
return `${FORM_FAILURE_COOKIE}=${encoded}; Path=/; Max-Age=60; SameSite=Lax${secure}`;
|
|
85
98
|
}
|
|
86
99
|
export function shouldRedirectFormFailure(event) {
|
|
87
100
|
return isUnsafeMethod(event) && acceptsHtml(event) && !isApiRequest(event);
|
|
@@ -96,7 +109,7 @@ export function applyFormFailureRedirect(event, response, failure) {
|
|
|
96
109
|
response.body = "";
|
|
97
110
|
setHeader(event, response, "location", getRedirectLocation(event));
|
|
98
111
|
setHeader(event, response, "content-type", "text/html; charset=utf-8");
|
|
99
|
-
appendSetCookie(event, response, serializeFormFailureCookie(failure));
|
|
112
|
+
appendSetCookie(event, response, serializeFormFailureCookie(event, failure));
|
|
100
113
|
}
|
|
101
114
|
export const formFailureInternals = {
|
|
102
115
|
appendSetCookie
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createError, getHeaders, getRequestHeader, getRequestURL } from "#imports";
|
|
2
|
+
import { HoloRequestBodyTooLargeError, readBoundedBufferedBody, readBoundedNodeRequestBody, requestBodyLimitFor } from "@holo-js/kernel";
|
|
3
|
+
async function readBoundedEventBody(event) {
|
|
4
|
+
const limit = requestBodyLimitFor(getRequestHeader(event, "content-type"));
|
|
5
|
+
try {
|
|
6
|
+
const node = event.node.req;
|
|
7
|
+
const buffered = await readBoundedBufferedBody(
|
|
8
|
+
event._requestBody || event.web?.request?.body || node[Symbol.for("h3RawBody")] || node.rawBody || node.body,
|
|
9
|
+
limit
|
|
10
|
+
);
|
|
11
|
+
if (buffered) return buffered;
|
|
12
|
+
const bytes = await readBoundedNodeRequestBody(event.node.req, limit, getRequestHeader(event, "content-length"));
|
|
13
|
+
event._requestBody = bytes;
|
|
14
|
+
Object.assign(event.node.req, { [Symbol.for("h3RawBody")]: bytes });
|
|
15
|
+
return { bytes };
|
|
16
|
+
} catch (error) {
|
|
17
|
+
if (error instanceof HoloRequestBodyTooLargeError) {
|
|
18
|
+
throw createError({ statusCode: error.status, statusMessage: "Payload Too Large", cause: error });
|
|
19
|
+
}
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export async function createWebRequest(event) {
|
|
24
|
+
const buffered = await readBoundedEventBody(event);
|
|
25
|
+
const headers = new Headers();
|
|
26
|
+
for (const [key, value] of Object.entries(getHeaders(event))) {
|
|
27
|
+
if (typeof value === "string") {
|
|
28
|
+
headers.set(key, value);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (buffered.contentType) {
|
|
32
|
+
headers.set("content-type", buffered.contentType);
|
|
33
|
+
}
|
|
34
|
+
return new Request(getRequestURL(event, { xForwardedProto: false }), {
|
|
35
|
+
body: buffered.bytes,
|
|
36
|
+
headers,
|
|
37
|
+
method: event.method
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { defineEventHandler
|
|
1
|
+
import { defineEventHandler } from "#imports";
|
|
2
2
|
import { renderBroadcastAuthResponse } from "@holo-js/broadcast/auth";
|
|
3
3
|
import { holo } from "../../composables/index.js";
|
|
4
|
+
import { createWebRequest } from "../route-request.js";
|
|
4
5
|
function resolveBroadcastSigning(connection) {
|
|
5
6
|
if (!connection || typeof connection !== "object") {
|
|
6
7
|
return {};
|
|
@@ -20,18 +21,7 @@ export default defineEventHandler(async (event) => {
|
|
|
20
21
|
const broadcast = app.config?.broadcast;
|
|
21
22
|
const connection = broadcast ? broadcast.connections[broadcast.default] : void 0;
|
|
22
23
|
const signing = resolveBroadcastSigning(connection);
|
|
23
|
-
|
|
24
|
-
for (const [key, value] of Object.entries(getHeaders(event))) {
|
|
25
|
-
if (typeof value === "string") {
|
|
26
|
-
headers.set(key, value);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
const request = new Request(getRequestURL(event), {
|
|
30
|
-
method: event.method,
|
|
31
|
-
headers,
|
|
32
|
-
body: await readRawBody(event)
|
|
33
|
-
});
|
|
34
|
-
return await renderBroadcastAuthResponse(request, {
|
|
24
|
+
return await renderBroadcastAuthResponse(await createWebRequest(event), {
|
|
35
25
|
...signing,
|
|
36
26
|
resolveUser: async (_request, context) => {
|
|
37
27
|
const guardAuth = context.guard ? auth?.guard(context.guard) : void 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineEventHandler } from "
|
|
1
|
+
import { defineEventHandler } from "#imports";
|
|
2
2
|
import { renderBroadcastClientConfigResponse } from "@holo-js/broadcast/client-config";
|
|
3
3
|
import { holo } from "../../composables/index.js";
|
|
4
4
|
export default defineEventHandler(async () => {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { defineEventHandler } from "
|
|
2
|
-
import { useRuntimeConfig } from "#imports";
|
|
1
|
+
import { defineEventHandler, useRuntimeConfig } from "#imports";
|
|
3
2
|
import { handleRealtimeMutationRequest } from "@holo-js/realtime/server";
|
|
4
|
-
import {
|
|
3
|
+
import { realtimeDefinitions } from "#holo/realtime-definitions";
|
|
4
|
+
import { createWebRequest } from "../route-request.js";
|
|
5
5
|
export default defineEventHandler(async (event) => {
|
|
6
|
-
return await handleRealtimeMutationRequest(await
|
|
7
|
-
projectRoot: useRuntimeConfig().holo.projectRoot ?? process.cwd()
|
|
6
|
+
return await handleRealtimeMutationRequest(await createWebRequest(event), {
|
|
7
|
+
projectRoot: useRuntimeConfig().holo.projectRoot ?? process.cwd(),
|
|
8
|
+
definitions: realtimeDefinitions
|
|
8
9
|
});
|
|
9
10
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { defineEventHandler } from "
|
|
2
|
-
import { useRuntimeConfig } from "#imports";
|
|
1
|
+
import { defineEventHandler, useRuntimeConfig } from "#imports";
|
|
3
2
|
import { handleRealtimeQueryRequest } from "@holo-js/realtime/server";
|
|
4
|
-
import {
|
|
3
|
+
import { realtimeDefinitions } from "#holo/realtime-definitions";
|
|
4
|
+
import { createWebRequest } from "../route-request.js";
|
|
5
5
|
export default defineEventHandler(async (event) => {
|
|
6
|
-
return await handleRealtimeQueryRequest(await
|
|
7
|
-
projectRoot: useRuntimeConfig().holo.projectRoot ?? process.cwd()
|
|
6
|
+
return await handleRealtimeQueryRequest(await createWebRequest(event), {
|
|
7
|
+
projectRoot: useRuntimeConfig().holo.projectRoot ?? process.cwd(),
|
|
8
|
+
definitions: realtimeDefinitions
|
|
8
9
|
});
|
|
9
10
|
});
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { createPublicStorageResponse } from "@holo-js/storage";
|
|
2
|
-
import { defineEventHandler,
|
|
2
|
+
import { defineEventHandler, getRequestURL, sendWebResponse } from "#imports";
|
|
3
3
|
import { holo } from "../../composables/index.js";
|
|
4
|
+
function createStorageRequest(event) {
|
|
5
|
+
return event.web?.request ?? new Request(getRequestURL(event, { xForwardedProto: false }), {
|
|
6
|
+
method: event.method,
|
|
7
|
+
headers: event.headers
|
|
8
|
+
});
|
|
9
|
+
}
|
|
4
10
|
export default defineEventHandler(async (event) => {
|
|
5
11
|
const app = await holo.getApp();
|
|
6
|
-
const response = await createPublicStorageResponse(app.projectRoot, app.config.storage,
|
|
12
|
+
const response = await createPublicStorageResponse(app.projectRoot, app.config.storage, createStorageRequest(event), app.config.app.key);
|
|
7
13
|
return sendWebResponse(event, response);
|
|
8
14
|
});
|
package/dist/runtime/shims.d.ts
CHANGED
|
@@ -1,45 +1,12 @@
|
|
|
1
|
-
import type { RuntimeConfigInput
|
|
2
|
-
|
|
3
|
-
type HoloConnectionRuntimeConfig = RuntimeConnectionConfig
|
|
4
|
-
type HoloDatabaseRuntimeConfig = RuntimeDatabaseConfig
|
|
5
|
-
|
|
6
|
-
interface StorageRuntimeDriverShim {
|
|
7
|
-
name: string
|
|
8
|
-
driver: 'local' | 'public' | 's3'
|
|
9
|
-
visibility: 'private' | 'public'
|
|
10
|
-
root?: string
|
|
11
|
-
url?: string
|
|
12
|
-
bucket?: string
|
|
13
|
-
region?: string
|
|
14
|
-
endpoint?: string
|
|
15
|
-
accessKeyId?: string
|
|
16
|
-
secretAccessKey?: string
|
|
17
|
-
sessionToken?: string
|
|
18
|
-
forcePathStyleEndpoint?: boolean
|
|
19
|
-
}
|
|
1
|
+
import type { RuntimeConfigInput } from '@holo-js/db'
|
|
20
2
|
|
|
21
3
|
interface HoloRuntimeConfig extends RuntimeConfigInput {
|
|
22
4
|
holo: NonNullable<RuntimeConfigInput['holo']> & {
|
|
23
|
-
appEnv: 'production' | 'development' | 'test'
|
|
24
|
-
appDebug: boolean
|
|
25
5
|
appUrl?: string
|
|
26
6
|
projectRoot?: string
|
|
27
7
|
}
|
|
28
|
-
db?: HoloDatabaseRuntimeConfig
|
|
29
|
-
holoStorage: {
|
|
30
|
-
defaultDisk: string | undefined
|
|
31
|
-
diskNames: string[]
|
|
32
|
-
routePrefix: string
|
|
33
|
-
disks: Record<string, StorageRuntimeDriverShim>
|
|
34
|
-
}
|
|
35
8
|
}
|
|
36
9
|
|
|
37
|
-
/**
|
|
38
|
-
* Minimal Nuxt runtime shims for adapter typechecking.
|
|
39
|
-
*
|
|
40
|
-
* Keep these declarations limited to the fields consumed by adapter runtime code:
|
|
41
|
-
* runtime config, storage access, and Nitro route/plugin globals.
|
|
42
|
-
*/
|
|
43
10
|
declare module '#app' {
|
|
44
11
|
export function defineNuxtPlugin<T>(plugin: T): T
|
|
45
12
|
export function useRuntimeConfig(): HoloRuntimeConfig
|
|
@@ -48,23 +15,9 @@ declare module '#app' {
|
|
|
48
15
|
|
|
49
16
|
declare module '#imports' {
|
|
50
17
|
export function useRuntimeConfig(): HoloRuntimeConfig
|
|
51
|
-
export function
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
declare module 'nitropack/runtime/config' {
|
|
55
|
-
export function useRuntimeConfig(): HoloRuntimeConfig
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
declare module 'nitropack/runtime/context' {
|
|
59
|
-
export function useEvent(): unknown
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
declare module 'nitropack/runtime/plugin' {
|
|
18
|
+
export function useEvent(): import('h3').H3Event
|
|
63
19
|
export function defineNitroPlugin<T>(plugin: T): T
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
declare module 'nitropack/runtime/storage' {
|
|
67
|
-
export function useStorage(base: string): unknown
|
|
20
|
+
export * from 'h3'
|
|
68
21
|
}
|
|
69
22
|
|
|
70
23
|
declare global {
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holo-js/adapter-nuxt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Holo-JS Framework - Nuxt adapter",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/cobraprojects/holo-js.git",
|
|
10
|
+
"directory": "packages/adapter-nuxt"
|
|
11
|
+
},
|
|
7
12
|
"exports": {
|
|
8
13
|
".": {
|
|
9
14
|
"types": "./dist/types.d.mts",
|
|
@@ -29,7 +34,8 @@
|
|
|
29
34
|
"main": "./dist/module.mjs",
|
|
30
35
|
"types": "./dist/types.d.mts",
|
|
31
36
|
"files": [
|
|
32
|
-
"dist"
|
|
37
|
+
"dist",
|
|
38
|
+
"LICENSE"
|
|
33
39
|
],
|
|
34
40
|
"scripts": {
|
|
35
41
|
"build": "nuxt-module-build build",
|
|
@@ -38,22 +44,22 @@
|
|
|
38
44
|
"test": "vitest --run"
|
|
39
45
|
},
|
|
40
46
|
"dependencies": {
|
|
41
|
-
"@holo-js/adapter-shared": "^0.
|
|
42
|
-
"@nuxt/kit": "^4.
|
|
43
|
-
"@holo-js/config": "^0.
|
|
44
|
-
"@holo-js/core": "^0.
|
|
45
|
-
"@holo-js/db": "^0.
|
|
46
|
-
"@holo-js/
|
|
47
|
-
"
|
|
48
|
-
"nitropack": "^2.13.4"
|
|
47
|
+
"@holo-js/adapter-shared": "^0.4.0",
|
|
48
|
+
"@nuxt/kit": "^4.6.2",
|
|
49
|
+
"@holo-js/config": "^0.4.0",
|
|
50
|
+
"@holo-js/core": "^0.4.0",
|
|
51
|
+
"@holo-js/db": "^0.4.0",
|
|
52
|
+
"@holo-js/kernel": "^0.4.0",
|
|
53
|
+
"@holo-js/validation": "^0.4.0"
|
|
49
54
|
},
|
|
50
55
|
"peerDependencies": {
|
|
56
|
+
"nuxt": "^4.6.2",
|
|
51
57
|
"vue": "^3.5.13",
|
|
52
|
-
"@holo-js/broadcast": "^0.
|
|
53
|
-
"@holo-js/forms": "^0.
|
|
54
|
-
"@holo-js/realtime": "^0.
|
|
55
|
-
"@holo-js/storage": "^0.
|
|
56
|
-
"@holo-js/storage-s3": "^0.
|
|
58
|
+
"@holo-js/broadcast": "^0.4.0",
|
|
59
|
+
"@holo-js/forms": "^0.4.0",
|
|
60
|
+
"@holo-js/realtime": "^0.4.0",
|
|
61
|
+
"@holo-js/storage": "^0.4.0",
|
|
62
|
+
"@holo-js/storage-s3": "^0.4.0"
|
|
57
63
|
},
|
|
58
64
|
"peerDependenciesMeta": {
|
|
59
65
|
"@holo-js/broadcast": {
|
|
@@ -74,17 +80,18 @@
|
|
|
74
80
|
},
|
|
75
81
|
"devDependencies": {
|
|
76
82
|
"vue": "^3.5.13",
|
|
77
|
-
"@holo-js/broadcast": "^0.
|
|
78
|
-
"@holo-js/realtime": "^0.
|
|
79
|
-
"@holo-js/storage-s3": "^0.
|
|
83
|
+
"@holo-js/broadcast": "^0.4.0",
|
|
84
|
+
"@holo-js/realtime": "^0.4.0",
|
|
85
|
+
"@holo-js/storage-s3": "^0.4.0",
|
|
80
86
|
"@nuxt/module-builder": "^1.0.2",
|
|
81
|
-
"@types/node": "^22.
|
|
82
|
-
"nuxt": "^4.
|
|
83
|
-
"typescript": "^
|
|
84
|
-
"vitest": "^
|
|
85
|
-
"@nuxt/nitro-server": "^4.
|
|
87
|
+
"@types/node": "^22.20.5",
|
|
88
|
+
"nuxt": "^4.6.2",
|
|
89
|
+
"typescript": "^6.0.3",
|
|
90
|
+
"vitest": "^5.0.5",
|
|
91
|
+
"@nuxt/nitro-server": "^4.6.2",
|
|
92
|
+
"h3": "^1.15.11"
|
|
86
93
|
},
|
|
87
94
|
"engines": {
|
|
88
|
-
"node": "^22.
|
|
95
|
+
"node": "^22.18.0 || ^24.0.0 || ^26.0.0"
|
|
89
96
|
}
|
|
90
97
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "@holo-js/storage-s3";
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { configureStorageRuntime } from "@holo-js/storage/runtime";
|
|
2
|
-
import { useRuntimeConfig } from "nitropack/runtime/config";
|
|
3
|
-
import { defineNitroPlugin } from "nitropack/runtime/plugin";
|
|
4
|
-
import { useStorage as useNitroStorage } from "nitropack/runtime/storage";
|
|
5
|
-
export default defineNitroPlugin(() => {
|
|
6
|
-
configureStorageRuntime({
|
|
7
|
-
getRuntimeConfig: () => useRuntimeConfig(),
|
|
8
|
-
getStorage: (base) => useNitroStorage(base)
|
|
9
|
-
});
|
|
10
|
-
});
|
|
File without changes
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { getHeaders, getRequestURL, readRawBody } from "h3";
|
|
2
|
-
export async function createRealtimeRequest(event) {
|
|
3
|
-
const headers = new Headers();
|
|
4
|
-
for (const [key, value] of Object.entries(getHeaders(event))) {
|
|
5
|
-
if (typeof value === "string") {
|
|
6
|
-
headers.set(key, value);
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
return new Request(getRequestURL(event), {
|
|
10
|
-
body: await readRawBody(event),
|
|
11
|
-
headers,
|
|
12
|
-
method: event.method
|
|
13
|
-
});
|
|
14
|
-
}
|
|
File without changes
|
|
File without changes
|