@holo-js/adapter-nuxt 0.1.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/dist/module.d.mts +19 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +95 -0
- package/dist/runtime/composables/forms.d.ts +11 -0
- package/dist/runtime/composables/forms.js +69 -0
- package/dist/runtime/composables/index.d.ts +41 -0
- package/dist/runtime/composables/index.js +96 -0
- package/dist/runtime/drivers/s3.d.ts +0 -0
- package/dist/runtime/drivers/s3.js +1 -0
- package/dist/runtime/plugins/init.d.ts +0 -0
- package/dist/runtime/plugins/init.js +21 -0
- package/dist/runtime/plugins/storage.d.ts +0 -0
- package/dist/runtime/plugins/storage.js +8 -0
- package/dist/runtime/server/imports/holo.d.ts +0 -0
- package/dist/runtime/server/imports/holo.js +6 -0
- package/dist/runtime/server/routes/storage.get.d.ts +0 -0
- package/dist/runtime/server/routes/storage.get.js +192 -0
- package/dist/runtime/server/utils/storage.d.ts +0 -0
- package/dist/runtime/server/utils/storage.js +1 -0
- package/dist/runtime/shims.d.ts +57 -0
- package/dist/types.d.mts +3 -0
- package/package.json +47 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LoadedHoloConfig, HoloConfigMap } from '@holo-js/config';
|
|
2
|
+
import { ModuleOptions as ModuleOptions$1 } from '@holo-js/storage';
|
|
3
|
+
|
|
4
|
+
type ModuleOptions = Record<string, never>;
|
|
5
|
+
declare function toStorageModuleOptions(loaded: LoadedHoloConfig<HoloConfigMap>): ModuleOptions$1;
|
|
6
|
+
declare const _default: {
|
|
7
|
+
meta?: {
|
|
8
|
+
name?: string;
|
|
9
|
+
};
|
|
10
|
+
defaults?: Partial<ModuleOptions> | undefined;
|
|
11
|
+
setup: (options: ModuleOptions, nuxt: unknown) => void | Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
declare const adapterNuxtInternals: {
|
|
15
|
+
toStorageModuleOptions: typeof toStorageModuleOptions;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { adapterNuxtInternals, _default as default };
|
|
19
|
+
export type { ModuleOptions };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { defineNuxtModule, createResolver, addServerPlugin, addImports, addServerImportsDir, addServerHandler } from '@nuxt/kit';
|
|
3
|
+
import { loadConfigDirectory } from '@holo-js/config';
|
|
4
|
+
import { mergeModuleOptions, normalizeModuleOptions, applyNitroStorageConfig, hasPublicLocalDisk } from '@holo-js/storage';
|
|
5
|
+
|
|
6
|
+
function toStorageModuleOptions(loaded) {
|
|
7
|
+
return {
|
|
8
|
+
defaultDisk: loaded.storage.defaultDisk,
|
|
9
|
+
routePrefix: loaded.storage.routePrefix,
|
|
10
|
+
disks: { ...loaded.storage.disks }
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const module$1 = defineNuxtModule({
|
|
14
|
+
meta: {
|
|
15
|
+
name: "@holo-js/adapter-nuxt"
|
|
16
|
+
},
|
|
17
|
+
async setup(_options, rawNuxt) {
|
|
18
|
+
const nuxt = rawNuxt;
|
|
19
|
+
const resolver = createResolver(import.meta.url);
|
|
20
|
+
const opts = nuxt.options;
|
|
21
|
+
const rootDir = opts.rootDir ?? opts.srcDir ?? process.cwd();
|
|
22
|
+
const sourceDir = opts.srcDir ?? rootDir;
|
|
23
|
+
const loaded = await loadConfigDirectory(rootDir, {
|
|
24
|
+
preferCache: process.env.NODE_ENV === "production",
|
|
25
|
+
processEnv: process.env
|
|
26
|
+
});
|
|
27
|
+
const loadedStorageOptions = toStorageModuleOptions(loaded);
|
|
28
|
+
const s3Driver = resolver.resolve("./runtime/drivers/s3.js");
|
|
29
|
+
opts.nitro = opts.nitro || { storage: {} };
|
|
30
|
+
opts.nitro.storage = opts.nitro.storage || {};
|
|
31
|
+
opts.runtimeConfig = opts.runtimeConfig || {};
|
|
32
|
+
opts.runtimeConfig.holo = {
|
|
33
|
+
appUrl: loaded.app.url,
|
|
34
|
+
appEnv: loaded.app.env,
|
|
35
|
+
appDebug: loaded.app.debug,
|
|
36
|
+
projectRoot: rootDir
|
|
37
|
+
};
|
|
38
|
+
opts.runtimeConfig.db = loaded.database;
|
|
39
|
+
const mergedStorageOptions = mergeModuleOptions(void 0, loadedStorageOptions);
|
|
40
|
+
const normalizedStorage = normalizeModuleOptions(mergedStorageOptions);
|
|
41
|
+
opts._holoStorageModuleOptions = mergedStorageOptions;
|
|
42
|
+
opts.runtimeConfig.holoStorage = normalizedStorage;
|
|
43
|
+
if (!opts._holoCoreRuntimeRegistered) {
|
|
44
|
+
addServerPlugin(resolver.resolve("./runtime/plugins/init"));
|
|
45
|
+
addImports([
|
|
46
|
+
{ name: "holo", as: "holo", from: resolver.resolve("./runtime/composables") },
|
|
47
|
+
{ name: "useHoloDb", as: "useHoloDb", from: resolver.resolve("./runtime/composables") },
|
|
48
|
+
{ name: "useHoloEnv", as: "useHoloEnv", from: resolver.resolve("./runtime/composables") },
|
|
49
|
+
{ name: "useHoloDebug", as: "useHoloDebug", from: resolver.resolve("./runtime/composables") },
|
|
50
|
+
{ name: "useStorage", as: "useStorage", from: resolver.resolve("./runtime/composables") },
|
|
51
|
+
{ name: "Storage", as: "Storage", from: resolver.resolve("./runtime/composables") }
|
|
52
|
+
]);
|
|
53
|
+
addServerImportsDir(resolver.resolve("./runtime/server/imports"));
|
|
54
|
+
addServerImportsDir(resolve(sourceDir, "server/models"));
|
|
55
|
+
opts._holoCoreRuntimeRegistered = true;
|
|
56
|
+
}
|
|
57
|
+
if (!opts._holoStorageRuntimeRegistered) {
|
|
58
|
+
addServerPlugin(resolver.resolve("./runtime/plugins/storage"));
|
|
59
|
+
opts._holoStorageRuntimeRegistered = true;
|
|
60
|
+
}
|
|
61
|
+
if (!opts.nitro.storage || Object.keys(opts.nitro.storage).every((key) => !key.startsWith("holo:"))) {
|
|
62
|
+
applyNitroStorageConfig(opts, normalizedStorage, s3Driver);
|
|
63
|
+
}
|
|
64
|
+
const runtimePath = resolver.resolve("./runtime");
|
|
65
|
+
if (!opts.build.transpile.includes(runtimePath)) {
|
|
66
|
+
opts.build.transpile.push(runtimePath);
|
|
67
|
+
}
|
|
68
|
+
if (!opts._holoStorageFinalizeRegistered) {
|
|
69
|
+
opts._holoStorageFinalizeRegistered = true;
|
|
70
|
+
nuxt.hook("modules:done", () => {
|
|
71
|
+
const finalNormalized = normalizeModuleOptions(opts._holoStorageModuleOptions);
|
|
72
|
+
opts.runtimeConfig = opts.runtimeConfig || {};
|
|
73
|
+
opts.runtimeConfig.holoStorage = finalNormalized;
|
|
74
|
+
applyNitroStorageConfig(opts, finalNormalized, s3Driver);
|
|
75
|
+
if (hasPublicLocalDisk(finalNormalized)) {
|
|
76
|
+
addServerHandler({
|
|
77
|
+
route: `${finalNormalized.routePrefix}/**`,
|
|
78
|
+
handler: resolver.resolve("./runtime/server/routes/storage.get")
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (!opts._holoTypesRegistered) {
|
|
84
|
+
opts._holoTypesRegistered = true;
|
|
85
|
+
nuxt.hook("prepare:types", ({ references }) => {
|
|
86
|
+
references.push({ types: "@holo-js/adapter-nuxt" });
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
const adapterNuxtInternals = {
|
|
92
|
+
toStorageModuleOptions
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export { adapterNuxtInternals, module$1 as default };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
ClientSubmitContext,
|
|
3
|
+
ClientSubmitResult,
|
|
4
|
+
FormFieldState,
|
|
5
|
+
FormFieldTree,
|
|
6
|
+
UseFormOptions,
|
|
7
|
+
UseFormResult,
|
|
8
|
+
ValidateOnMode,
|
|
9
|
+
} from '@holo-js/forms/client'
|
|
10
|
+
|
|
11
|
+
export declare const useForm: typeof import('@holo-js/forms/client').useForm
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { onScopeDispose, shallowRef, watchEffect } from "vue";
|
|
2
|
+
import {
|
|
3
|
+
useForm as createForm
|
|
4
|
+
} from "@holo-js/forms/client";
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Blob);
|
|
7
|
+
}
|
|
8
|
+
function createReactiveView(targetRef, version, cache) {
|
|
9
|
+
const target = targetRef.value;
|
|
10
|
+
const cached = cache.get(target);
|
|
11
|
+
if (cached) {
|
|
12
|
+
return cached;
|
|
13
|
+
}
|
|
14
|
+
const proxy = new Proxy({}, {
|
|
15
|
+
get(_shell, key) {
|
|
16
|
+
void version.value;
|
|
17
|
+
const currentTarget = targetRef.value;
|
|
18
|
+
const value = Reflect.get(currentTarget, key);
|
|
19
|
+
if (typeof value === "function") {
|
|
20
|
+
return value.bind(currentTarget);
|
|
21
|
+
}
|
|
22
|
+
if (isPlainObject(value)) {
|
|
23
|
+
return createReactiveView({ value }, version, cache);
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
},
|
|
27
|
+
set(_shell, key, value) {
|
|
28
|
+
const updated = Reflect.set(targetRef.value, key, value);
|
|
29
|
+
version.value += 1;
|
|
30
|
+
return updated;
|
|
31
|
+
},
|
|
32
|
+
ownKeys() {
|
|
33
|
+
void version.value;
|
|
34
|
+
return Reflect.ownKeys(targetRef.value);
|
|
35
|
+
},
|
|
36
|
+
getOwnPropertyDescriptor(_shell, key) {
|
|
37
|
+
void version.value;
|
|
38
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(targetRef.value, key);
|
|
39
|
+
if (!descriptor) {
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
...descriptor,
|
|
44
|
+
configurable: true
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
has(_shell, key) {
|
|
48
|
+
void version.value;
|
|
49
|
+
return Reflect.has(targetRef.value, key);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
cache.set(target, proxy);
|
|
53
|
+
return proxy;
|
|
54
|
+
}
|
|
55
|
+
export function useForm(schemaDefinition, options = {}) {
|
|
56
|
+
const form = shallowRef(createForm(schemaDefinition, options));
|
|
57
|
+
const version = shallowRef(0);
|
|
58
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
59
|
+
const stopWatching = watchEffect((onCleanup) => {
|
|
60
|
+
form.value = createForm(schemaDefinition, options);
|
|
61
|
+
version.value += 1;
|
|
62
|
+
const unsubscribe = form.value.subscribe(() => {
|
|
63
|
+
version.value += 1;
|
|
64
|
+
});
|
|
65
|
+
onCleanup(unsubscribe);
|
|
66
|
+
});
|
|
67
|
+
onScopeDispose(stopWatching);
|
|
68
|
+
return createReactiveView(form, version, cache);
|
|
69
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export {
|
|
2
|
+
Storage,
|
|
3
|
+
configureStorageRuntime,
|
|
4
|
+
resetStorageRuntime,
|
|
5
|
+
useStorage,
|
|
6
|
+
type StorageBackend,
|
|
7
|
+
type StorageContent,
|
|
8
|
+
type StorageDisk,
|
|
9
|
+
type StorageInstance,
|
|
10
|
+
type StorageRuntimeBindings,
|
|
11
|
+
type TemporaryUrlOptions,
|
|
12
|
+
} from '@holo-js/storage/runtime'
|
|
13
|
+
|
|
14
|
+
export interface HoloRuntimeConnection {
|
|
15
|
+
driver?: 'sqlite' | 'postgres' | 'mysql'
|
|
16
|
+
url?: string
|
|
17
|
+
host?: string
|
|
18
|
+
port?: number | string
|
|
19
|
+
username?: string
|
|
20
|
+
password?: string
|
|
21
|
+
database?: string
|
|
22
|
+
schema?: string
|
|
23
|
+
ssl?: boolean | Record<string, unknown>
|
|
24
|
+
logging: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface HoloRuntimeDatabaseGroup {
|
|
28
|
+
defaultConnection: string
|
|
29
|
+
connections: Record<string, { url?: string } | HoloRuntimeConnection>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface HoloRuntimeDefaultConnection extends HoloRuntimeConnection {
|
|
33
|
+
defaultConnection: 'default'
|
|
34
|
+
connections: {
|
|
35
|
+
default: HoloRuntimeConnection
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export declare function useHoloDb(): HoloRuntimeDatabaseGroup | HoloRuntimeDefaultConnection
|
|
40
|
+
export declare function useHoloEnv(): 'production' | 'development' | 'test'
|
|
41
|
+
export declare function useHoloDebug(): boolean
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export {
|
|
2
|
+
Storage,
|
|
3
|
+
configureStorageRuntime,
|
|
4
|
+
resetStorageRuntime,
|
|
5
|
+
useStorage
|
|
6
|
+
} from "@holo-js/storage/runtime";
|
|
7
|
+
import {
|
|
8
|
+
createHoloProjectAccessors,
|
|
9
|
+
initializeHoloAdapterProject
|
|
10
|
+
} from "@holo-js/core";
|
|
11
|
+
export function configureHoloRuntimeConfig(config) {
|
|
12
|
+
const runtimeGlobals = globalThis;
|
|
13
|
+
runtimeGlobals.__holoRuntimeConfig = config;
|
|
14
|
+
}
|
|
15
|
+
export function resetHoloRuntimeConfig() {
|
|
16
|
+
const runtimeGlobals = globalThis;
|
|
17
|
+
delete runtimeGlobals.__holoRuntimeConfig;
|
|
18
|
+
}
|
|
19
|
+
function getRuntimeConfig() {
|
|
20
|
+
const runtimeGlobals = globalThis;
|
|
21
|
+
if (runtimeGlobals.__holoRuntimeConfig) {
|
|
22
|
+
return runtimeGlobals.__holoRuntimeConfig;
|
|
23
|
+
}
|
|
24
|
+
if (typeof runtimeGlobals.useRuntimeConfig !== "function") {
|
|
25
|
+
throw new TypeError("Holo runtime config is not configured.");
|
|
26
|
+
}
|
|
27
|
+
return runtimeGlobals.useRuntimeConfig();
|
|
28
|
+
}
|
|
29
|
+
function resolveRuntimeEnvName(env) {
|
|
30
|
+
return env;
|
|
31
|
+
}
|
|
32
|
+
function resolveRuntimeProjectRoot(config) {
|
|
33
|
+
return config.holo.projectRoot?.trim() || process.cwd();
|
|
34
|
+
}
|
|
35
|
+
export const holo = createHoloProjectAccessors(async () => {
|
|
36
|
+
const config = getRuntimeConfig();
|
|
37
|
+
return initializeHoloAdapterProject(resolveRuntimeProjectRoot(config), {
|
|
38
|
+
envName: resolveRuntimeEnvName(config.holo.appEnv),
|
|
39
|
+
preferCache: process.env.NODE_ENV === "production",
|
|
40
|
+
processEnv: process.env
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
function resolveDefaultConnectionName(group) {
|
|
44
|
+
if (group.defaultConnection) {
|
|
45
|
+
return group.defaultConnection;
|
|
46
|
+
}
|
|
47
|
+
const connectionNames = Object.keys(group.connections);
|
|
48
|
+
if (connectionNames.includes("default")) {
|
|
49
|
+
return "default";
|
|
50
|
+
}
|
|
51
|
+
return connectionNames[0] ?? "default";
|
|
52
|
+
}
|
|
53
|
+
function normalizeConnection(connection) {
|
|
54
|
+
const driver = connection.driver ?? (connection.filename ? "sqlite" : void 0);
|
|
55
|
+
const database = connection.database ?? connection.filename;
|
|
56
|
+
const url = connection.url ?? (driver === "sqlite" ? database : void 0);
|
|
57
|
+
return {
|
|
58
|
+
driver,
|
|
59
|
+
url,
|
|
60
|
+
host: connection.host,
|
|
61
|
+
port: connection.port,
|
|
62
|
+
username: connection.username,
|
|
63
|
+
password: connection.password,
|
|
64
|
+
database,
|
|
65
|
+
schema: connection.schema,
|
|
66
|
+
ssl: connection.ssl,
|
|
67
|
+
logging: connection.logging ?? false
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export function useHoloDb() {
|
|
71
|
+
const config = getRuntimeConfig();
|
|
72
|
+
const group = config.db ?? { connections: {} };
|
|
73
|
+
const connections = group.connections ?? {};
|
|
74
|
+
return {
|
|
75
|
+
defaultConnection: resolveDefaultConnectionName({
|
|
76
|
+
defaultConnection: group.defaultConnection,
|
|
77
|
+
connections
|
|
78
|
+
}),
|
|
79
|
+
connections: Object.fromEntries(
|
|
80
|
+
Object.entries(connections).map(([name, connection]) => {
|
|
81
|
+
if (typeof connection === "string") {
|
|
82
|
+
return [name, { url: connection }];
|
|
83
|
+
}
|
|
84
|
+
return [name, normalizeConnection(connection)];
|
|
85
|
+
})
|
|
86
|
+
)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export function useHoloEnv() {
|
|
90
|
+
const config = getRuntimeConfig();
|
|
91
|
+
return resolveRuntimeEnvName(config.holo.appEnv);
|
|
92
|
+
}
|
|
93
|
+
export function useHoloDebug() {
|
|
94
|
+
const config = getRuntimeConfig();
|
|
95
|
+
return config.holo.appDebug;
|
|
96
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@holo-js/storage/runtime/drivers/s3";
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
configureHoloRuntimeConfig
|
|
3
|
+
} from "../composables/index.js";
|
|
4
|
+
import { initializeHoloAdapterProject } from "@holo-js/core";
|
|
5
|
+
export default defineNitroPlugin(async (nitroApp) => {
|
|
6
|
+
const config = useRuntimeConfig();
|
|
7
|
+
configureHoloRuntimeConfig(config);
|
|
8
|
+
const project = await initializeHoloAdapterProject(config.holo.projectRoot?.trim() || process.cwd(), {
|
|
9
|
+
envName: config.holo.appEnv,
|
|
10
|
+
preferCache: process.env.NODE_ENV === "production",
|
|
11
|
+
processEnv: process.env
|
|
12
|
+
});
|
|
13
|
+
const driver = project.runtime.manager.connection().getDriver();
|
|
14
|
+
console.log(`\u2705 Holo DB connected (${driver})`);
|
|
15
|
+
nitroApp.hooks.hook("close", async () => {
|
|
16
|
+
try {
|
|
17
|
+
await project.runtime.shutdown();
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
});
|
|
File without changes
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { configureStorageRuntime } from "@holo-js/storage/runtime";
|
|
2
|
+
import { useRuntimeConfig, useStorage as useNitroStorage } from "#imports";
|
|
3
|
+
export default defineNitroPlugin(() => {
|
|
4
|
+
configureStorageRuntime({
|
|
5
|
+
getRuntimeConfig: () => useRuntimeConfig(),
|
|
6
|
+
getStorage: (base) => useNitroStorage(base)
|
|
7
|
+
});
|
|
8
|
+
});
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { readFile, realpath } from "node:fs/promises";
|
|
2
|
+
import { extname, resolve, sep } from "node:path";
|
|
3
|
+
import { useRuntimeConfig } from "#imports";
|
|
4
|
+
const NAMED_PUBLIC_DISK_ROUTE_SEGMENT = "__holo";
|
|
5
|
+
function normalizeRequestPath(value) {
|
|
6
|
+
return value.split("/").map((segment) => {
|
|
7
|
+
const trimmed = segment.trim();
|
|
8
|
+
if (!trimmed) {
|
|
9
|
+
return trimmed;
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
return decodeURIComponent(trimmed);
|
|
13
|
+
} catch {
|
|
14
|
+
return trimmed;
|
|
15
|
+
}
|
|
16
|
+
}).filter(Boolean);
|
|
17
|
+
}
|
|
18
|
+
function isPublicLocalDisk(disk) {
|
|
19
|
+
return Boolean(disk && disk.visibility === "public" && disk.driver !== "s3" && typeof disk.root === "string");
|
|
20
|
+
}
|
|
21
|
+
function isMissingFileError(error) {
|
|
22
|
+
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
23
|
+
return Boolean(
|
|
24
|
+
error && typeof error === "object" && (code === "ENOENT" || code === "EISDIR" || code === "ENOTDIR")
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
function resolveAbsolutePath(disk, fileSegments) {
|
|
28
|
+
const root = resolve(disk.root);
|
|
29
|
+
const absolutePath = resolve(root, ...fileSegments);
|
|
30
|
+
if (absolutePath !== root && !absolutePath.startsWith(`${root}${sep}`)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return absolutePath;
|
|
34
|
+
}
|
|
35
|
+
function resolveContentType(absolutePath) {
|
|
36
|
+
switch (extname(absolutePath).toLowerCase()) {
|
|
37
|
+
case ".avif":
|
|
38
|
+
return "image/avif";
|
|
39
|
+
case ".css":
|
|
40
|
+
return "text/css; charset=utf-8";
|
|
41
|
+
case ".gif":
|
|
42
|
+
return "image/gif";
|
|
43
|
+
case ".html":
|
|
44
|
+
return "text/html; charset=utf-8";
|
|
45
|
+
case ".jpeg":
|
|
46
|
+
case ".jpg":
|
|
47
|
+
return "image/jpeg";
|
|
48
|
+
case ".js":
|
|
49
|
+
case ".mjs":
|
|
50
|
+
return "text/javascript; charset=utf-8";
|
|
51
|
+
case ".json":
|
|
52
|
+
return "application/json; charset=utf-8";
|
|
53
|
+
case ".mp3":
|
|
54
|
+
return "audio/mpeg";
|
|
55
|
+
case ".pdf":
|
|
56
|
+
return "application/pdf";
|
|
57
|
+
case ".png":
|
|
58
|
+
return "image/png";
|
|
59
|
+
case ".svg":
|
|
60
|
+
return "image/svg+xml";
|
|
61
|
+
case ".txt":
|
|
62
|
+
return "text/plain; charset=utf-8";
|
|
63
|
+
case ".webp":
|
|
64
|
+
return "image/webp";
|
|
65
|
+
case ".woff":
|
|
66
|
+
return "font/woff";
|
|
67
|
+
case ".woff2":
|
|
68
|
+
return "font/woff2";
|
|
69
|
+
default:
|
|
70
|
+
return "application/octet-stream";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function createMissingFileError(message) {
|
|
74
|
+
const error = new Error(message);
|
|
75
|
+
error.code = "ENOENT";
|
|
76
|
+
return error;
|
|
77
|
+
}
|
|
78
|
+
function isPathWithinRoot(root, absolutePath) {
|
|
79
|
+
return absolutePath === root || absolutePath.startsWith(`${root}${sep}`);
|
|
80
|
+
}
|
|
81
|
+
async function readPublicFile(event, disk, absolutePath) {
|
|
82
|
+
const resolvedRoot = await realpath(resolve(disk.root));
|
|
83
|
+
const resolvedPath = await realpath(absolutePath);
|
|
84
|
+
if (!isPathWithinRoot(resolvedRoot, resolvedPath)) {
|
|
85
|
+
throw createMissingFileError("Storage file not found.");
|
|
86
|
+
}
|
|
87
|
+
const contents = await readFile(absolutePath);
|
|
88
|
+
setResponseHeader(event, "content-type", resolveContentType(absolutePath));
|
|
89
|
+
return contents;
|
|
90
|
+
}
|
|
91
|
+
function resolveRouteSegments(routePath) {
|
|
92
|
+
const segments = normalizeRequestPath(routePath);
|
|
93
|
+
if (segments.length === 0 || segments.includes("..")) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
return segments;
|
|
97
|
+
}
|
|
98
|
+
function resolveDefaultPublicStorageRequest(config, segments) {
|
|
99
|
+
const disk = isPublicLocalDisk(config.disks.public) ? config.disks.public : void 0;
|
|
100
|
+
if (!disk) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const absolutePath = resolveAbsolutePath(disk, segments);
|
|
104
|
+
if (!absolutePath) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
return { disk, absolutePath };
|
|
108
|
+
}
|
|
109
|
+
function usesReservedNamedDiskNamespace(segments) {
|
|
110
|
+
return segments[0] === NAMED_PUBLIC_DISK_ROUTE_SEGMENT;
|
|
111
|
+
}
|
|
112
|
+
function resolveNamedPublicStorageRequest(config, segments) {
|
|
113
|
+
const namedPublicDisks = Object.values(config.disks).filter((disk2) => {
|
|
114
|
+
return isPublicLocalDisk(disk2) && disk2.name !== "public";
|
|
115
|
+
});
|
|
116
|
+
const usesReservedNamespace = segments[0] === NAMED_PUBLIC_DISK_ROUTE_SEGMENT;
|
|
117
|
+
const diskName = usesReservedNamespace ? segments[1] : segments[0];
|
|
118
|
+
const disk = diskName ? namedPublicDisks.find((candidate) => candidate.name === diskName) : void 0;
|
|
119
|
+
if (!disk) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const fileSegments = usesReservedNamespace ? segments.slice(2) : segments.slice(1);
|
|
123
|
+
if (fileSegments.length === 0) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const absolutePath = resolveAbsolutePath(disk, fileSegments);
|
|
127
|
+
if (!absolutePath) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
return { disk, absolutePath };
|
|
131
|
+
}
|
|
132
|
+
export function resolvePublicStorageRequest(config, routePath) {
|
|
133
|
+
const segments = resolveRouteSegments(routePath);
|
|
134
|
+
if (!segments) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
if (usesReservedNamedDiskNamespace(segments)) {
|
|
138
|
+
return resolveNamedPublicStorageRequest(config, segments) ?? resolveDefaultPublicStorageRequest(config, segments);
|
|
139
|
+
}
|
|
140
|
+
return resolveDefaultPublicStorageRequest(config, segments) ?? resolveNamedPublicStorageRequest(config, segments);
|
|
141
|
+
}
|
|
142
|
+
function resolveFallbackPublicStorageRequest(config, segments, attemptedDiskName) {
|
|
143
|
+
if (usesReservedNamedDiskNamespace(segments)) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const candidates = [
|
|
147
|
+
resolveDefaultPublicStorageRequest(config, segments),
|
|
148
|
+
resolveNamedPublicStorageRequest(config, segments)
|
|
149
|
+
];
|
|
150
|
+
return candidates.find((candidate) => candidate && candidate.disk.name !== attemptedDiskName) ?? null;
|
|
151
|
+
}
|
|
152
|
+
export default defineEventHandler(async (event) => {
|
|
153
|
+
const runtimeConfig = useRuntimeConfig();
|
|
154
|
+
const { holoStorage } = runtimeConfig;
|
|
155
|
+
const pathname = getRequestURL(event).pathname;
|
|
156
|
+
const routePath = pathname.startsWith(holoStorage.routePrefix) ? pathname.slice(holoStorage.routePrefix.length) : pathname;
|
|
157
|
+
const segments = resolveRouteSegments(routePath);
|
|
158
|
+
if (!segments) {
|
|
159
|
+
throw createError({
|
|
160
|
+
statusCode: 404,
|
|
161
|
+
statusMessage: "Storage file not found."
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
const resolved = resolvePublicStorageRequest(holoStorage, routePath);
|
|
165
|
+
if (!resolved) {
|
|
166
|
+
throw createError({
|
|
167
|
+
statusCode: 404,
|
|
168
|
+
statusMessage: "Storage file not found."
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
return await readPublicFile(event, resolved.disk, resolved.absolutePath);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (!isMissingFileError(error)) {
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
const fallback = resolveFallbackPublicStorageRequest(holoStorage, segments, resolved.disk.name);
|
|
178
|
+
if (fallback) {
|
|
179
|
+
try {
|
|
180
|
+
return await readPublicFile(event, fallback.disk, fallback.absolutePath);
|
|
181
|
+
} catch (fallbackError) {
|
|
182
|
+
if (!isMissingFileError(fallbackError)) {
|
|
183
|
+
throw fallbackError;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
throw createError({
|
|
188
|
+
statusCode: 404,
|
|
189
|
+
statusMessage: "Storage file not found."
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
});
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Storage, useStorage } from "@holo-js/storage/runtime";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { RuntimeConfigInput, RuntimeConnectionConfig, RuntimeDatabaseConfig } from '@holo-js/db'
|
|
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
|
+
}
|
|
20
|
+
|
|
21
|
+
interface HoloRuntimeConfig extends RuntimeConfigInput {
|
|
22
|
+
holo: NonNullable<RuntimeConfigInput['holo']> & {
|
|
23
|
+
appEnv: 'production' | 'development' | 'test'
|
|
24
|
+
appDebug: boolean
|
|
25
|
+
appUrl?: string
|
|
26
|
+
projectRoot?: string
|
|
27
|
+
}
|
|
28
|
+
db?: HoloDatabaseRuntimeConfig
|
|
29
|
+
holoStorage: {
|
|
30
|
+
defaultDisk: string | undefined
|
|
31
|
+
diskNames: string[]
|
|
32
|
+
routePrefix: string
|
|
33
|
+
disks: Record<string, StorageRuntimeDriverShim>
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
declare module '#app' {
|
|
38
|
+
export function useRuntimeConfig(): HoloRuntimeConfig
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
declare module '#imports' {
|
|
42
|
+
export function useRuntimeConfig(): HoloRuntimeConfig
|
|
43
|
+
export function useStorage(base: string): unknown
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare global {
|
|
47
|
+
function createError(input: { statusCode: number, statusMessage: string }): Error
|
|
48
|
+
function defineNitroPlugin<T>(plugin: T): T
|
|
49
|
+
function defineEventHandler<T>(
|
|
50
|
+
handler: (event: unknown) => T | Promise<T>,
|
|
51
|
+
): (event: unknown) => T | Promise<T>
|
|
52
|
+
function getRequestURL(event: unknown): URL
|
|
53
|
+
function setResponseHeader(event: unknown, name: string, value: string): void
|
|
54
|
+
function useRuntimeConfig(): HoloRuntimeConfig
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export {}
|
package/dist/types.d.mts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@holo-js/adapter-nuxt",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Holo-JS Framework - Nuxt adapter",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/types.d.mts",
|
|
10
|
+
"import": "./dist/module.mjs"
|
|
11
|
+
},
|
|
12
|
+
"./client": {
|
|
13
|
+
"types": "./dist/runtime/composables/forms.d.ts",
|
|
14
|
+
"import": "./dist/runtime/composables/forms.js"
|
|
15
|
+
},
|
|
16
|
+
"./runtime": {
|
|
17
|
+
"types": "./dist/runtime/composables/index.d.ts",
|
|
18
|
+
"import": "./dist/runtime/composables/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"main": "./dist/module.mjs",
|
|
22
|
+
"types": "./dist/types.d.mts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "nuxt-module-build build",
|
|
28
|
+
"stub": "nuxt-module-build build",
|
|
29
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
30
|
+
"test": "vitest --run"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@nuxt/kit": "catalog:",
|
|
34
|
+
"@holo-js/config": "workspace:*",
|
|
35
|
+
"@holo-js/core": "workspace:*",
|
|
36
|
+
"@holo-js/db": "workspace:*",
|
|
37
|
+
"@holo-js/forms": "workspace:*",
|
|
38
|
+
"@holo-js/storage": "workspace:*"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@nuxt/module-builder": "catalog:",
|
|
42
|
+
"@types/node": "catalog:",
|
|
43
|
+
"nuxt": "catalog:",
|
|
44
|
+
"typescript": "catalog:",
|
|
45
|
+
"vitest": "catalog:"
|
|
46
|
+
}
|
|
47
|
+
}
|