@holo-js/adapter-nuxt 0.1.3 → 0.1.5

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 CHANGED
@@ -22,10 +22,74 @@ type StorageModuleOptions = {
22
22
  routePrefix?: string;
23
23
  disks?: Record<string, StorageDiskConfig>;
24
24
  };
25
+ type RuntimeDiskConfig = {
26
+ name: string;
27
+ driver: StorageDriver;
28
+ visibility: StorageVisibility;
29
+ root?: string;
30
+ url?: string;
31
+ bucket?: string;
32
+ region?: string;
33
+ endpoint?: string;
34
+ accessKeyId?: string;
35
+ secretAccessKey?: string;
36
+ sessionToken?: string;
37
+ forcePathStyleEndpoint?: boolean;
38
+ };
39
+ type HoloStorageRuntimeConfig = {
40
+ defaultDisk: string | undefined;
41
+ diskNames: string[];
42
+ routePrefix: string;
43
+ disks: Record<string, RuntimeDiskConfig>;
44
+ };
25
45
  type StorageS3Module = {
26
46
  default: unknown;
27
47
  };
28
48
  declare function hasModuleNotFoundCode(error: unknown, expectedSpecifier: string): boolean;
49
+ declare function isModuleResolutionFailure(error: unknown): boolean;
50
+ type ViteOptimizeDepsOptions = {
51
+ include?: string[];
52
+ [key: string]: unknown;
53
+ };
54
+ type NuxtViteOptions = {
55
+ optimizeDeps?: ViteOptimizeDepsOptions;
56
+ [key: string]: unknown;
57
+ };
58
+ interface NuxtOptionsWithNitro {
59
+ nitro: {
60
+ storage: Record<string, unknown>;
61
+ errorHandler?: string | string[];
62
+ experimental?: {
63
+ asyncContext?: boolean;
64
+ [key: string]: unknown;
65
+ };
66
+ [key: string]: unknown;
67
+ };
68
+ runtimeConfig: {
69
+ public?: {
70
+ holo?: {
71
+ appName?: string;
72
+ [key: string]: unknown;
73
+ };
74
+ [key: string]: unknown;
75
+ };
76
+ holoStorage?: HoloStorageRuntimeConfig;
77
+ [key: string]: unknown;
78
+ };
79
+ build: {
80
+ transpile: string[];
81
+ };
82
+ vite?: NuxtViteOptions;
83
+ srcDir: string;
84
+ rootDir?: string;
85
+ _holoStorageModuleOptions?: StorageModuleOptions;
86
+ _holoStorageFinalizeRegistered?: boolean;
87
+ _holoStorageRuntimeRegistered?: boolean;
88
+ _holoCoreRuntimeRegistered?: boolean;
89
+ _holoTypesRegistered?: boolean;
90
+ }
91
+ declare function resolveClientOptimizeDeps(rootDir: string): string[];
92
+ declare function addViteOptimizeDeps(opts: NuxtOptionsWithNitro, deps: readonly string[]): void;
29
93
  declare function importOptionalStorageS3Module(): Promise<StorageS3Module | undefined>;
30
94
  declare function hasLoadedConfigFile(loaded: LoadedHoloConfig<HoloConfigMap>, configName: string): boolean;
31
95
  declare function toStorageModuleOptions(loaded: LoadedHoloConfig<HoloConfigMap>): StorageModuleOptions;
@@ -38,9 +102,12 @@ declare const _default: {
38
102
  };
39
103
 
40
104
  declare const moduleInternals: {
105
+ addViteOptimizeDeps: typeof addViteOptimizeDeps;
41
106
  hasModuleNotFoundCode: typeof hasModuleNotFoundCode;
42
107
  hasLoadedConfigFile: typeof hasLoadedConfigFile;
43
108
  importOptionalStorageS3Module: typeof importOptionalStorageS3Module;
109
+ isModuleResolutionFailure: typeof isModuleResolutionFailure;
110
+ resolveClientOptimizeDeps: typeof resolveClientOptimizeDeps;
44
111
  };
45
112
  declare const adapterNuxtInternals: {
46
113
  toStorageModuleOptions: typeof toStorageModuleOptions;
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@holo-js/adapter-nuxt",
3
3
  "configKey": "@holo-js/adapter-nuxt",
4
- "version": "0.1.3",
4
+ "version": "0.1.5",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -1,7 +1,21 @@
1
- import { resolve } from 'node:path';
2
- import { defineNuxtModule, createResolver, addServerPlugin, addImports, addServerImportsDir, addServerHandler } from '@nuxt/kit';
1
+ import { readdir, mkdir, writeFile, access } from 'node:fs/promises';
2
+ import { createRequire } from 'node:module';
3
+ import { resolve, extname, relative, basename, dirname } from 'node:path';
4
+ import { defineNuxtModule, createResolver, addServerPlugin, addServerImportsDir, addServerHandler } from '@nuxt/kit';
3
5
  import { loadConfigDirectory } from '@holo-js/config';
4
6
 
7
+ const MODEL_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"]);
8
+ const HOLO_PACKAGE_SCOPE = "@holo-js/";
9
+ const CLIENT_OPTIMIZE_DEPS = [
10
+ {
11
+ packageName: `${HOLO_PACKAGE_SCOPE}forms`,
12
+ include: `${HOLO_PACKAGE_SCOPE}forms > ${HOLO_PACKAGE_SCOPE}validation > valibot`
13
+ },
14
+ {
15
+ packageName: `${HOLO_PACKAGE_SCOPE}validation`,
16
+ include: `${HOLO_PACKAGE_SCOPE}validation > valibot`
17
+ }
18
+ ];
5
19
  function escapeRegExp(value) {
6
20
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7
21
  }
@@ -26,6 +40,47 @@ function hasModuleNotFoundCode(error, expectedSpecifier) {
26
40
  }
27
41
  return false;
28
42
  }
43
+ function isModuleResolutionFailure(error) {
44
+ if (!error || typeof error !== "object") {
45
+ return false;
46
+ }
47
+ return "code" in error && (error.code === "MODULE_NOT_FOUND" || error.code === "ERR_MODULE_NOT_FOUND");
48
+ }
49
+ function resolveClientOptimizeDeps(rootDir) {
50
+ const projectRequire = createRequire(resolve(rootDir, "package.json"));
51
+ const deps = [];
52
+ for (const { packageName, include } of CLIENT_OPTIMIZE_DEPS) {
53
+ try {
54
+ projectRequire.resolve(packageName);
55
+ deps.push(include);
56
+ } catch (error) {
57
+ if (!isModuleResolutionFailure(error)) {
58
+ throw error;
59
+ }
60
+ }
61
+ }
62
+ return deps;
63
+ }
64
+ function addViteOptimizeDeps(opts, deps) {
65
+ if (deps.length === 0) {
66
+ return;
67
+ }
68
+ opts.vite = opts.vite || {};
69
+ opts.vite.optimizeDeps = opts.vite.optimizeDeps || {};
70
+ opts.vite.optimizeDeps.include = [
71
+ .../* @__PURE__ */ new Set([
72
+ ...opts.vite.optimizeDeps.include || [],
73
+ ...deps
74
+ ])
75
+ ];
76
+ }
77
+ function addNitroErrorHandler(opts, handler) {
78
+ const current = opts.nitro.errorHandler;
79
+ const handlers = Array.isArray(current) ? current : typeof current === "string" ? [current] : [];
80
+ if (!handlers.includes(handler)) {
81
+ opts.nitro.errorHandler = [...handlers, handler];
82
+ }
83
+ }
29
84
  async function importOptionalStorageModule() {
30
85
  try {
31
86
  return await import('@holo-js/storage');
@@ -53,6 +108,15 @@ function hasLoadedConfigFile(loaded, configName) {
53
108
  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`);
54
109
  });
55
110
  }
111
+ async function ensureModelRegistryTypesPlaceholder(path) {
112
+ try {
113
+ await access(path);
114
+ return;
115
+ } catch {
116
+ await mkdir(dirname(path), { recursive: true });
117
+ await writeFile(path, "// Generated by holo prepare. Do not edit.\n\nexport {}\n", "utf8");
118
+ }
119
+ }
56
120
  function toStorageModuleOptions(loaded) {
57
121
  return {
58
122
  defaultDisk: loaded.storage.defaultDisk,
@@ -60,6 +124,50 @@ function toStorageModuleOptions(loaded) {
60
124
  disks: { ...loaded.storage.disks }
61
125
  };
62
126
  }
127
+ async function createServerModelImports(sourceDir, modelsRelativePath, generatedSchemaRelativePath) {
128
+ const modelsDir = resolve(sourceDir, modelsRelativePath);
129
+ const generatedSchemaPath = resolve(sourceDir, generatedSchemaRelativePath);
130
+ const modelImportDir = resolve(sourceDir, ".holo-js/generated/nuxt-server-imports");
131
+ const modelImportFile = resolve(modelImportDir, "models.ts");
132
+ const modelPluginFile = resolve(modelImportDir, "plugin.ts");
133
+ let modelFiles;
134
+ try {
135
+ modelFiles = (await readdir(modelsDir)).filter((fileName) => MODEL_FILE_EXTENSIONS.has(extname(fileName))).sort((left, right) => left.localeCompare(right));
136
+ } catch {
137
+ return null;
138
+ }
139
+ if (modelFiles.length === 0) {
140
+ return null;
141
+ }
142
+ const generatedSchemaImportPath = relative(modelImportDir, generatedSchemaPath).replaceAll("\\", "/");
143
+ const normalizedGeneratedSchemaImportPath = generatedSchemaImportPath.replace(/^(?!\.)/, "./");
144
+ const lines = [
145
+ `import '${normalizedGeneratedSchemaImportPath.slice(0, -extname(normalizedGeneratedSchemaImportPath).length)}'`,
146
+ "",
147
+ ...modelFiles.map((fileName) => {
148
+ const modelName = basename(fileName, extname(fileName));
149
+ const importPath = relative(modelImportDir, resolve(modelsDir, fileName)).replaceAll("\\", "/");
150
+ const normalizedImportPath = importPath.replace(/^(?!\.)/, "./");
151
+ const extension = extname(normalizedImportPath);
152
+ return `export { default as ${modelName} } from '${normalizedImportPath.slice(0, -extension.length)}'`;
153
+ })
154
+ ];
155
+ const pluginLines = [
156
+ `import '${normalizedGeneratedSchemaImportPath.slice(0, -extname(normalizedGeneratedSchemaImportPath).length)}'`,
157
+ "import './models'",
158
+ "",
159
+ "export default () => {}"
160
+ ];
161
+ await mkdir(modelImportDir, { recursive: true });
162
+ await writeFile(modelImportFile, `${lines.join("\n")}
163
+ `, "utf8");
164
+ await writeFile(modelPluginFile, `${pluginLines.join("\n")}
165
+ `, "utf8");
166
+ return {
167
+ importDir: modelImportDir,
168
+ pluginFile: modelPluginFile
169
+ };
170
+ }
63
171
  const module$1 = defineNuxtModule({
64
172
  meta: {
65
173
  name: "@holo-js/adapter-nuxt"
@@ -70,6 +178,10 @@ const module$1 = defineNuxtModule({
70
178
  const opts = nuxt.options;
71
179
  const rootDir = opts.rootDir ?? opts.srcDir ?? process.cwd();
72
180
  const sourceDir = opts.srcDir ?? rootDir;
181
+ const authTypesPath = resolve(rootDir, ".holo-js/generated/auth.d.ts");
182
+ const authorizationTypesPath = resolve(rootDir, ".holo-js/generated/authorization/types.d.ts");
183
+ const modelRegistryTypesPath = resolve(rootDir, ".holo-js/generated/model-registry.d.ts");
184
+ addViteOptimizeDeps(opts, resolveClientOptimizeDeps(rootDir));
73
185
  const loaded = await loadConfigDirectory(rootDir, {
74
186
  preferCache: process.env.NODE_ENV === "production",
75
187
  processEnv: process.env
@@ -79,7 +191,17 @@ const module$1 = defineNuxtModule({
79
191
  const s3Driver = resolver.resolve("./runtime/drivers/s3.js");
80
192
  opts.nitro = opts.nitro || { storage: {} };
81
193
  opts.nitro.storage = opts.nitro.storage || {};
194
+ addNitroErrorHandler(opts, resolver.resolve("./runtime/server/error"));
195
+ opts.nitro.experimental = {
196
+ ...opts.nitro.experimental || {},
197
+ asyncContext: true
198
+ };
82
199
  opts.runtimeConfig = opts.runtimeConfig || {};
200
+ opts.runtimeConfig.public = opts.runtimeConfig.public || {};
201
+ opts.runtimeConfig.public.holo = {
202
+ ...opts.runtimeConfig.public.holo || {},
203
+ appName: loaded.app.name
204
+ };
83
205
  opts.runtimeConfig.holo = {
84
206
  appUrl: loaded.app.url,
85
207
  appEnv: loaded.app.env,
@@ -103,22 +225,18 @@ const module$1 = defineNuxtModule({
103
225
  opts.runtimeConfig.holoStorage = normalizedStorage;
104
226
  }
105
227
  if (!opts._holoCoreRuntimeRegistered) {
106
- const imports = [
107
- { name: "holo", as: "holo", from: resolver.resolve("./runtime/composables") },
108
- { name: "useHoloDb", as: "useHoloDb", from: resolver.resolve("./runtime/composables") },
109
- { name: "useHoloEnv", as: "useHoloEnv", from: resolver.resolve("./runtime/composables") },
110
- { name: "useHoloDebug", as: "useHoloDebug", from: resolver.resolve("./runtime/composables") }
111
- ];
112
- if (storageModule) {
113
- imports.push(
114
- { name: "useStorage", as: "useStorage", from: resolver.resolve("./runtime/composables/storage") },
115
- { name: "Storage", as: "Storage", from: resolver.resolve("./runtime/composables/storage") }
116
- );
117
- }
118
228
  addServerPlugin(resolver.resolve("./runtime/plugins/init"));
119
- addImports(imports);
120
- addServerImportsDir(resolver.resolve("./runtime/server/imports"));
121
- addServerImportsDir(resolve(sourceDir, "server/models"));
229
+ addServerPlugin(resolver.resolve("./runtime/plugins/forms"));
230
+ addServerImportsDir(resolver.resolve("./runtime/server/auto-imports"));
231
+ const serverModelImports = await createServerModelImports(
232
+ sourceDir,
233
+ loaded.app.paths.models,
234
+ loaded.app.paths.generatedSchema
235
+ );
236
+ if (serverModelImports) {
237
+ addServerImportsDir(serverModelImports.importDir);
238
+ addServerPlugin(serverModelImports.pluginFile);
239
+ }
122
240
  opts._holoCoreRuntimeRegistered = true;
123
241
  }
124
242
  if (storageModule && !opts._holoStorageRuntimeRegistered) {
@@ -149,16 +267,25 @@ const module$1 = defineNuxtModule({
149
267
  }
150
268
  if (!opts._holoTypesRegistered) {
151
269
  opts._holoTypesRegistered = true;
270
+ await ensureModelRegistryTypesPlaceholder(authTypesPath);
271
+ await ensureModelRegistryTypesPlaceholder(authorizationTypesPath);
272
+ await ensureModelRegistryTypesPlaceholder(modelRegistryTypesPath);
152
273
  nuxt.hook("prepare:types", ({ references }) => {
153
274
  references.push({ types: "@holo-js/adapter-nuxt" });
275
+ references.push({ path: authTypesPath });
276
+ references.push({ path: authorizationTypesPath });
277
+ references.push({ path: modelRegistryTypesPath });
154
278
  });
155
279
  }
156
280
  }
157
281
  });
158
282
  const moduleInternals = {
283
+ addViteOptimizeDeps,
159
284
  hasModuleNotFoundCode,
160
285
  hasLoadedConfigFile,
161
- importOptionalStorageS3Module
286
+ importOptionalStorageS3Module,
287
+ isModuleResolutionFailure,
288
+ resolveClientOptimizeDeps
162
289
  };
163
290
  const adapterNuxtInternals = {
164
291
  toStorageModuleOptions
File without changes
@@ -0,0 +1,8 @@
1
+ import { createError } from "h3";
2
+ export function createNuxtAuthorizationError(decision) {
3
+ const status = decision.status === 404 ? 404 : 403;
4
+ return createError({
5
+ statusCode: status,
6
+ statusMessage: decision.message ?? "You are not authorized to perform this action."
7
+ });
8
+ }
@@ -1,11 +1,27 @@
1
+ import type { FormSchema, InferFormData } from '@holo-js/forms'
2
+ import type { ValidationErrorBag } from '@holo-js/forms'
3
+ import type {
4
+ InferFormFieldTree,
5
+ UseFormOptions,
6
+ UseFormResult,
7
+ } from '@holo-js/forms/internal/client'
8
+
1
9
  export type {
2
10
  ClientSubmitContext,
3
11
  ClientSubmitResult,
4
12
  FormFieldState,
5
13
  FormFieldTree,
14
+ InferFormFieldTree,
6
15
  UseFormOptions,
7
16
  UseFormResult,
8
17
  ValidateOnMode,
9
- } from '@holo-js/forms/client'
18
+ } from '@holo-js/forms/internal/client'
19
+
20
+ export declare function useForm<TSchema extends FormSchema, TSuccess = unknown>(
21
+ schemaDefinition: TSchema,
22
+ options?: UseFormOptions<InferFormData<TSchema>, TSuccess>,
23
+ ): UseFormResult<InferFormData<TSchema>, TSuccess, InferFormFieldTree<TSchema>>
10
24
 
11
- export declare const useForm: typeof import('@holo-js/forms/client').useForm
25
+ export declare function useValidationErrors<TData = Record<string, unknown>>(
26
+ bag?: string,
27
+ ): ValidationErrorBag<TData>
@@ -1,69 +1,287 @@
1
- import { onScopeDispose, shallowRef, watchEffect } from "vue";
1
+ import { onScopeDispose, reactive, shallowRef, watchEffect } from "vue";
2
+ import { useCookie } from "#app";
3
+ import { DEFAULT_VALIDATION_BAG, createErrorBag } from "@holo-js/validation";
2
4
  import {
3
- useForm as createForm
4
- } from "@holo-js/forms/client";
5
+ createFormClient
6
+ } from "@holo-js/forms/internal/client";
7
+ const FORM_FAILURE_COOKIE = "holo_form_failure";
8
+ const ARRAY_MUTATION_METHODS = /* @__PURE__ */ new Set([
9
+ "copyWithin",
10
+ "fill",
11
+ "pop",
12
+ "push",
13
+ "reverse",
14
+ "shift",
15
+ "sort",
16
+ "splice",
17
+ "unshift"
18
+ ]);
5
19
  function isPlainObject(value) {
6
20
  return !!value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Blob);
7
21
  }
8
- function createReactiveView(targetRef, version, cache) {
9
- const target = targetRef.value;
10
- const cached = cache.get(target);
22
+ function isLeafValue(value) {
23
+ return value instanceof Date || value instanceof Blob || !Array.isArray(value) && !isPlainObject(value);
24
+ }
25
+ function readBrowserCookie(name) {
26
+ const cookie = globalThis.document?.cookie;
27
+ if (!cookie) {
28
+ return void 0;
29
+ }
30
+ for (const segment of cookie.split(";")) {
31
+ const trimmed = segment.trim();
32
+ const separator = trimmed.indexOf("=");
33
+ if (separator <= 0) {
34
+ continue;
35
+ }
36
+ if (trimmed.slice(0, separator) === name) {
37
+ return trimmed.slice(separator + 1);
38
+ }
39
+ }
40
+ return void 0;
41
+ }
42
+ function readCookie(name) {
43
+ try {
44
+ const cookie = useCookie(name);
45
+ if (typeof cookie.value === "string" || isPlainObject(cookie.value)) {
46
+ return cookie.value;
47
+ }
48
+ } catch {
49
+ return readBrowserCookie(name);
50
+ }
51
+ return readBrowserCookie(name);
52
+ }
53
+ function parseFlashedValidationPayload() {
54
+ const value = readCookie(FORM_FAILURE_COOKIE);
55
+ if (!value) {
56
+ return void 0;
57
+ }
58
+ try {
59
+ const decoded = typeof value === "string" ? JSON.parse(decodeURIComponent(value)) : value;
60
+ if (!isPlainObject(decoded) || !isPlainObject(decoded.errors)) {
61
+ return void 0;
62
+ }
63
+ return {
64
+ bag: typeof decoded.bag === "string" ? decoded.bag : DEFAULT_VALIDATION_BAG,
65
+ errors: decoded.errors
66
+ };
67
+ } catch {
68
+ return void 0;
69
+ }
70
+ }
71
+ export function useValidationErrors(bag = DEFAULT_VALIDATION_BAG) {
72
+ const payload = parseFlashedValidationPayload();
73
+ if (!payload || (payload.bag ?? DEFAULT_VALIDATION_BAG) !== bag) {
74
+ return createErrorBag();
75
+ }
76
+ return createErrorBag(payload.errors ?? {});
77
+ }
78
+ function getValueAtPath(root, path) {
79
+ const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
80
+ let cursor = root;
81
+ for (const part of parts) {
82
+ if (!isPlainObject(cursor) && !Array.isArray(cursor)) {
83
+ return void 0;
84
+ }
85
+ cursor = cursor[part];
86
+ }
87
+ return cursor;
88
+ }
89
+ function createArrayMutationView(source, path, getForm, version, cache) {
90
+ const cached = cache.get(source);
11
91
  if (cached) {
12
92
  return cached;
13
93
  }
14
- const proxy = new Proxy({}, {
15
- get(_shell, key) {
94
+ const proxy = new Proxy([], {
95
+ get(_target, key) {
16
96
  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);
97
+ const current = getValueAtPath(getForm().values, path);
98
+ if (!Array.isArray(current)) {
99
+ return void 0;
21
100
  }
22
- if (isPlainObject(value)) {
23
- return createReactiveView({ value }, version, cache);
101
+ const value = Reflect.get(current, key, current);
102
+ if (typeof value !== "function") {
103
+ return value;
24
104
  }
25
- return value;
105
+ if (typeof key === "string" && ARRAY_MUTATION_METHODS.has(key)) {
106
+ return (...args) => {
107
+ const latest = getValueAtPath(getForm().values, path);
108
+ if (!Array.isArray(latest)) {
109
+ return void 0;
110
+ }
111
+ const next = latest.slice();
112
+ const result = Reflect.get(next, key, next).apply(next, args);
113
+ void getForm().setValue(path, next);
114
+ return result;
115
+ };
116
+ }
117
+ return value.bind(current);
26
118
  },
27
- set(_shell, key, value) {
28
- const updated = Reflect.set(targetRef.value, key, value);
29
- version.value += 1;
119
+ set(_target, key, value) {
120
+ const current = getValueAtPath(getForm().values, path);
121
+ if (!Array.isArray(current)) {
122
+ return false;
123
+ }
124
+ const next = current.slice();
125
+ const updated = Reflect.set(next, key, value);
126
+ if (updated) {
127
+ void getForm().setValue(path, next);
128
+ }
30
129
  return updated;
31
130
  },
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;
131
+ deleteProperty(_target, key) {
132
+ const current = getValueAtPath(getForm().values, path);
133
+ if (!Array.isArray(current)) {
134
+ return false;
41
135
  }
42
- return {
43
- ...descriptor,
44
- configurable: true
45
- };
46
- },
47
- has(_shell, key) {
48
- void version.value;
49
- return Reflect.has(targetRef.value, key);
136
+ const next = current.slice();
137
+ const deleted = Reflect.deleteProperty(next, key);
138
+ if (deleted) {
139
+ void getForm().setValue(path, next);
140
+ }
141
+ return deleted;
50
142
  }
51
143
  });
52
- cache.set(target, proxy);
144
+ cache.set(source, proxy);
53
145
  return proxy;
54
146
  }
147
+ function defineLeafAccessor(target, key, path, getForm, version, arrayCache) {
148
+ const descriptor = Object.getOwnPropertyDescriptor(target, key);
149
+ if (descriptor?.get && descriptor?.set) {
150
+ return;
151
+ }
152
+ Object.defineProperty(target, key, {
153
+ enumerable: true,
154
+ configurable: true,
155
+ get() {
156
+ void version.value;
157
+ const value = getValueAtPath(getForm().values, path);
158
+ return Array.isArray(value) ? createArrayMutationView(value, path, getForm, version, arrayCache) : value;
159
+ },
160
+ set(nextValue) {
161
+ void getForm().setValue(path, nextValue);
162
+ }
163
+ });
164
+ }
165
+ function syncValuesView(target, source, getForm, version, arrayCache, prefix = "") {
166
+ if (!isPlainObject(source)) {
167
+ for (const key of Object.keys(target)) {
168
+ delete target[key];
169
+ }
170
+ return;
171
+ }
172
+ for (const key of Object.keys(target)) {
173
+ if (!(key in source)) {
174
+ delete target[key];
175
+ }
176
+ }
177
+ for (const [key, item] of Object.entries(source)) {
178
+ const path = prefix ? `${prefix}.${key}` : key;
179
+ const current = target[key];
180
+ if (Array.isArray(item)) {
181
+ if (isPlainObject(current)) {
182
+ delete target[key];
183
+ }
184
+ defineLeafAccessor(target, key, path, getForm, version, arrayCache);
185
+ continue;
186
+ }
187
+ if (isLeafValue(item)) {
188
+ if (isPlainObject(current)) {
189
+ delete target[key];
190
+ }
191
+ defineLeafAccessor(target, key, path, getForm, version, arrayCache);
192
+ continue;
193
+ }
194
+ if (!isPlainObject(current)) {
195
+ target[key] = {};
196
+ }
197
+ syncValuesView(target[key], item, getForm, version, arrayCache, path);
198
+ }
199
+ }
55
200
  export function useForm(schemaDefinition, options = {}) {
56
- const form = shallowRef(createForm(schemaDefinition, options));
201
+ const form = shallowRef(void 0);
57
202
  const version = shallowRef(0);
58
- const cache = /* @__PURE__ */ new WeakMap();
203
+ let versionCounter = 0;
204
+ const rawValues = {};
205
+ const values = reactive(rawValues);
206
+ const arrayCache = /* @__PURE__ */ new WeakMap();
207
+ let activeForm;
208
+ const getActiveForm = () => {
209
+ if (!activeForm) {
210
+ throw new TypeError("Expected form to be initialized.");
211
+ }
212
+ return activeForm;
213
+ };
214
+ const currentForm = () => {
215
+ const current = form.value;
216
+ if (!current) {
217
+ throw new TypeError("Expected form to be initialized.");
218
+ }
219
+ return current;
220
+ };
221
+ const syncValuesFromForm = (localForm) => {
222
+ activeForm = localForm;
223
+ syncValuesView(
224
+ rawValues,
225
+ localForm.values,
226
+ getActiveForm,
227
+ version,
228
+ arrayCache
229
+ );
230
+ };
59
231
  const stopWatching = watchEffect((onCleanup) => {
60
- form.value = createForm(schemaDefinition, options);
61
- version.value += 1;
62
- const unsubscribe = form.value.subscribe(() => {
63
- version.value += 1;
232
+ const localForm = createFormClient(schemaDefinition, options);
233
+ syncValuesFromForm(localForm);
234
+ form.value = localForm;
235
+ version.value = ++versionCounter;
236
+ const unsubscribe = localForm.subscribe(() => {
237
+ syncValuesFromForm(localForm);
238
+ version.value = ++versionCounter;
64
239
  });
65
240
  onCleanup(unsubscribe);
66
241
  });
67
242
  onScopeDispose(stopWatching);
68
- return createReactiveView(form, version, cache);
243
+ return reactive({
244
+ get fields() {
245
+ void version.value;
246
+ return currentForm().fields;
247
+ },
248
+ values,
249
+ get errors() {
250
+ void version.value;
251
+ return currentForm().errors;
252
+ },
253
+ get submitting() {
254
+ void version.value;
255
+ return currentForm().submitting;
256
+ },
257
+ get valid() {
258
+ void version.value;
259
+ return currentForm().valid;
260
+ },
261
+ get lastSubmission() {
262
+ void version.value;
263
+ return currentForm().lastSubmission;
264
+ },
265
+ subscribe(listener) {
266
+ return currentForm().subscribe(listener);
267
+ },
268
+ async validate() {
269
+ return await currentForm().validate();
270
+ },
271
+ async validateField(path) {
272
+ return await currentForm().validateField(path);
273
+ },
274
+ async submit() {
275
+ return await currentForm().submit();
276
+ },
277
+ reset(nextValues) {
278
+ currentForm().reset(nextValues);
279
+ },
280
+ async setValue(path, value) {
281
+ await currentForm().setValue(path, value);
282
+ },
283
+ applyServerState(result) {
284
+ return currentForm().applyServerState(result);
285
+ }
286
+ });
69
287
  }
@@ -1,3 +1,5 @@
1
+ import type { HoloAdapterProjectAccessors } from '@holo-js/core'
2
+
1
3
  export interface HoloRuntimeConnection {
2
4
  driver?: 'sqlite' | 'postgres' | 'mysql'
3
5
  url?: string
@@ -23,6 +25,7 @@ export interface HoloRuntimeDefaultConnection extends HoloRuntimeConnection {
23
25
  }
24
26
  }
25
27
 
28
+ export declare const holo: HoloAdapterProjectAccessors
26
29
  export declare function useHoloDb(): HoloRuntimeDatabaseGroup | HoloRuntimeDefaultConnection
27
30
  export declare function useHoloEnv(): 'production' | 'development' | 'test'
28
31
  export declare function useHoloDebug(): boolean
@@ -2,6 +2,7 @@ import {
2
2
  createHoloProjectAccessors,
3
3
  initializeHoloAdapterProject
4
4
  } from "@holo-js/core";
5
+ import { createNuxtAuthorizationError } from "../authorization-error.js";
5
6
  export function configureHoloRuntimeConfig(config) {
6
7
  const runtimeGlobals = globalThis;
7
8
  runtimeGlobals.__holoRuntimeConfig = config;
@@ -26,12 +27,115 @@ function resolveRuntimeEnvName(env) {
26
27
  function resolveRuntimeProjectRoot(config) {
27
28
  return config.holo.projectRoot?.trim() || process.cwd();
28
29
  }
30
+ async function loadNitroContextModule() {
31
+ return await import("nitropack/runtime/context");
32
+ }
33
+ export function createNuxtAuthRequestAccessors() {
34
+ function safeDecode(value) {
35
+ try {
36
+ return decodeURIComponent(value);
37
+ } catch {
38
+ return void 0;
39
+ }
40
+ }
41
+ async function readHeader(name) {
42
+ const nitroContext = await loadNitroContextModule();
43
+ const event = nitroContext.useEvent();
44
+ if (!event) {
45
+ return void 0;
46
+ }
47
+ const normalizedName = name.toLowerCase();
48
+ if (event.headers instanceof Headers) {
49
+ return event.headers.get(name) ?? void 0;
50
+ }
51
+ if (event.request?.headers instanceof Headers) {
52
+ return event.request.headers.get(name) ?? void 0;
53
+ }
54
+ const value = event.node?.req?.headers?.[normalizedName];
55
+ if (Array.isArray(value)) {
56
+ return value[0];
57
+ }
58
+ return typeof value === "string" ? value : void 0;
59
+ }
60
+ async function readCookie(name) {
61
+ const header = await readHeader("cookie");
62
+ if (!header) {
63
+ return void 0;
64
+ }
65
+ for (const segment of header.split(";")) {
66
+ const trimmed = segment.trim();
67
+ const separator = trimmed.indexOf("=");
68
+ if (separator <= 0) {
69
+ continue;
70
+ }
71
+ const key = safeDecode(trimmed.slice(0, separator));
72
+ if (typeof key === "undefined") {
73
+ continue;
74
+ }
75
+ if (key !== name) {
76
+ continue;
77
+ }
78
+ const value = safeDecode(trimmed.slice(separator + 1));
79
+ if (typeof value === "undefined") {
80
+ continue;
81
+ }
82
+ return value;
83
+ }
84
+ return void 0;
85
+ }
86
+ async function appendCookie(cookie) {
87
+ const nitroContext = await loadNitroContextModule();
88
+ const event = nitroContext.useEvent();
89
+ const response = event?.node?.res;
90
+ if (!response) {
91
+ return;
92
+ }
93
+ const current = response.getHeader("set-cookie");
94
+ if (Array.isArray(current)) {
95
+ response.setHeader("set-cookie", [...current, cookie]);
96
+ return;
97
+ }
98
+ if (typeof current === "string") {
99
+ response.setHeader("set-cookie", [current, cookie]);
100
+ return;
101
+ }
102
+ response.setHeader("set-cookie", [cookie]);
103
+ }
104
+ const getCookieValue = async (name) => {
105
+ return await readCookie(name);
106
+ };
107
+ const getHeaderValue = async (name) => {
108
+ return await readHeader(name);
109
+ };
110
+ const appendResponseCookie = async (cookie) => {
111
+ await appendCookie(cookie);
112
+ };
113
+ const redirectResponse = async (url, status) => {
114
+ const nitroContext = await loadNitroContextModule();
115
+ const event = nitroContext.useEvent();
116
+ if (!event) {
117
+ throw new TypeError("Holo Nuxt auth redirect requires an active Nitro event.");
118
+ }
119
+ const { sendRedirect } = await import("h3");
120
+ await sendRedirect(event, url, status);
121
+ };
122
+ return {
123
+ getCookie: getCookieValue,
124
+ getHeader: getHeaderValue,
125
+ appendResponseCookie,
126
+ redirectResponse
127
+ };
128
+ }
29
129
  export const holo = createHoloProjectAccessors(async () => {
30
130
  const config = getRuntimeConfig();
31
131
  return initializeHoloAdapterProject(resolveRuntimeProjectRoot(config), {
32
132
  envName: resolveRuntimeEnvName(config.holo.appEnv),
33
133
  preferCache: process.env.NODE_ENV === "production",
34
- processEnv: process.env
134
+ processEnv: process.env,
135
+ authRequest: createNuxtAuthRequestAccessors(),
136
+ authorizationError: {
137
+ createError: createNuxtAuthorizationError
138
+ }
35
139
  });
36
140
  });
37
141
  function resolveDefaultConnectionName(group) {
File without changes
@@ -0,0 +1,18 @@
1
+ import { defineNitroPlugin } from "nitropack/runtime/plugin";
2
+ import {
3
+ applyFormFailureRedirect,
4
+ getFormFailurePayload,
5
+ shouldRedirectFormFailure
6
+ } from "../server/form-failure.js";
7
+ export default defineNitroPlugin((nitroApp) => {
8
+ nitroApp.hooks.hook("beforeResponse", (event, response) => {
9
+ if (!shouldRedirectFormFailure(event)) {
10
+ return;
11
+ }
12
+ const failure = getFormFailurePayload(response.body);
13
+ if (!failure) {
14
+ return;
15
+ }
16
+ applyFormFailureRedirect(event, response, failure);
17
+ });
18
+ });
@@ -1,14 +1,22 @@
1
1
  import {
2
+ createNuxtAuthRequestAccessors,
2
3
  configureHoloRuntimeConfig
3
4
  } from "../composables/index.js";
5
+ import { createNuxtAuthorizationError } from "../authorization-error.js";
4
6
  import { initializeHoloAdapterProject } from "@holo-js/core";
7
+ import { useRuntimeConfig } from "nitropack/runtime/config";
8
+ import { defineNitroPlugin } from "nitropack/runtime/plugin";
5
9
  export default defineNitroPlugin(async (nitroApp) => {
6
10
  const config = useRuntimeConfig();
7
11
  configureHoloRuntimeConfig(config);
8
12
  const project = await initializeHoloAdapterProject(config.holo.projectRoot?.trim() || process.cwd(), {
9
13
  envName: config.holo.appEnv,
10
14
  preferCache: process.env.NODE_ENV === "production",
11
- processEnv: process.env
15
+ processEnv: process.env,
16
+ authRequest: createNuxtAuthRequestAccessors(),
17
+ authorizationError: {
18
+ createError: createNuxtAuthorizationError
19
+ }
12
20
  });
13
21
  const driver = project.runtime.manager.connection().getDriver();
14
22
  console.log(`\u2705 Holo DB connected (${driver})`);
@@ -1,5 +1,7 @@
1
1
  import { configureStorageRuntime } from "@holo-js/storage/runtime";
2
- import { useRuntimeConfig, useStorage as useNitroStorage } from "#imports";
2
+ import { useRuntimeConfig } from "nitropack/runtime/config";
3
+ import { defineNitroPlugin } from "nitropack/runtime/plugin";
4
+ import { useStorage as useNitroStorage } from "nitropack/runtime/storage";
3
5
  export default defineNitroPlugin(() => {
4
6
  configureStorageRuntime({
5
7
  getRuntimeConfig: () => useRuntimeConfig(),
File without changes
@@ -0,0 +1,10 @@
1
+ import {
2
+ holo as baseHolo,
3
+ useHoloDb as baseUseHoloDb,
4
+ useHoloDebug as baseUseHoloDebug,
5
+ useHoloEnv as baseUseHoloEnv
6
+ } from "../../composables/index.js";
7
+ export const holo = baseHolo;
8
+ export const useHoloDb = baseUseHoloDb;
9
+ export const useHoloDebug = baseUseHoloDebug;
10
+ export const useHoloEnv = baseUseHoloEnv;
File without changes
@@ -0,0 +1,32 @@
1
+ import { send, setHeader, setResponseStatus } from "h3";
2
+ import { isValidationException } from "@holo-js/validation";
3
+ import {
4
+ applyFormFailureRedirect,
5
+ shouldRedirectFormFailure
6
+ } from "./form-failure.js";
7
+ function findValidationException(error) {
8
+ if (isValidationException(error)) {
9
+ return error;
10
+ }
11
+ if (!error || typeof error !== "object" || !("cause" in error)) {
12
+ return void 0;
13
+ }
14
+ return findValidationException(error.cause);
15
+ }
16
+ export default defineNitroErrorHandler(async (error, event) => {
17
+ const validationError = findValidationException(error);
18
+ if (!validationError) {
19
+ return;
20
+ }
21
+ const payload = validationError.toJSON();
22
+ if (shouldRedirectFormFailure(event)) {
23
+ const failure = { ...payload };
24
+ const response = {};
25
+ applyFormFailureRedirect(event, response, failure);
26
+ await send(event, "", "text/html");
27
+ return;
28
+ }
29
+ setResponseStatus(event, payload.status);
30
+ setHeader(event, "content-type", "application/json; charset=utf-8");
31
+ await send(event, JSON.stringify(payload), "application/json");
32
+ });
File without changes
@@ -0,0 +1,100 @@
1
+ const FORM_FAILURE_COOKIE = "holo_form_failure";
2
+ function getHeader(event, name) {
3
+ const value = event.node?.req?.headers?.[name.toLowerCase()];
4
+ return Array.isArray(value) ? value[0] : value;
5
+ }
6
+ function isFormFailurePayload(value) {
7
+ if (!value || typeof value !== "object") {
8
+ return false;
9
+ }
10
+ const payload = value;
11
+ return payload.ok === false && payload.valid === false && typeof payload.status === "number" && !!payload.errors && typeof payload.errors === "object";
12
+ }
13
+ export function getFormFailurePayload(value) {
14
+ if (isFormFailurePayload(value)) {
15
+ return value;
16
+ }
17
+ if (typeof value !== "string") {
18
+ return void 0;
19
+ }
20
+ try {
21
+ const parsed = JSON.parse(value);
22
+ return isFormFailurePayload(parsed) ? parsed : void 0;
23
+ } catch {
24
+ return void 0;
25
+ }
26
+ }
27
+ function acceptsHtml(event) {
28
+ return getHeader(event, "accept")?.toLowerCase().includes("text/html") === true;
29
+ }
30
+ function isUnsafeMethod(event) {
31
+ const method = event.node?.req?.method?.toUpperCase();
32
+ return method !== "GET" && method !== "HEAD";
33
+ }
34
+ function isApiRequest(event) {
35
+ const path = event.path ?? event.node?.req?.url ?? "";
36
+ return path.startsWith("/api/");
37
+ }
38
+ function getRedirectLocation(event) {
39
+ const referer = getHeader(event, "referer");
40
+ const host = getHeader(event, "host") ?? "localhost";
41
+ if (referer) {
42
+ try {
43
+ const url = new URL(referer);
44
+ if (url.host === host) {
45
+ return `${url.pathname}${url.search}`;
46
+ }
47
+ } catch {
48
+ }
49
+ }
50
+ return event.path ?? event.node?.req?.url ?? "/";
51
+ }
52
+ function appendSetCookie(event, response, cookie) {
53
+ const responseCookie = event.node?.res?.getHeader?.("set-cookie");
54
+ if (Array.isArray(responseCookie)) {
55
+ event.node?.res?.setHeader?.("set-cookie", [...responseCookie, cookie]);
56
+ } else if (responseCookie) {
57
+ event.node?.res?.setHeader?.("set-cookie", [String(responseCookie), cookie]);
58
+ } else {
59
+ event.node?.res?.setHeader?.("set-cookie", cookie);
60
+ }
61
+ if (response.headers instanceof Headers) {
62
+ response.headers.append("set-cookie", cookie);
63
+ } else {
64
+ response.headers ??= {};
65
+ const current = response.headers["set-cookie"];
66
+ if (Array.isArray(current)) {
67
+ response.headers["set-cookie"] = [...current.map(String), cookie];
68
+ return;
69
+ }
70
+ response.headers["set-cookie"] = current ? [String(current), cookie] : cookie;
71
+ }
72
+ }
73
+ function setHeader(event, response, name, value) {
74
+ event.node?.res?.setHeader?.(name, value);
75
+ if (response.headers instanceof Headers) {
76
+ response.headers.set(name, value);
77
+ return;
78
+ }
79
+ response.headers ??= {};
80
+ response.headers[name] = value;
81
+ }
82
+ function serializeFormFailureCookie(payload) {
83
+ const encoded = encodeURIComponent(JSON.stringify(payload));
84
+ return `${FORM_FAILURE_COOKIE}=${encoded}; Path=/; Max-Age=60; SameSite=Lax`;
85
+ }
86
+ export function shouldRedirectFormFailure(event) {
87
+ return isUnsafeMethod(event) && acceptsHtml(event) && !isApiRequest(event);
88
+ }
89
+ export function applyFormFailureRedirect(event, response, failure) {
90
+ if (event.node?.res) {
91
+ event.node.res.statusCode = 303;
92
+ event.node.res.statusMessage = "See Other";
93
+ }
94
+ response.statusCode = 303;
95
+ response.statusMessage = "See Other";
96
+ response.body = "";
97
+ setHeader(event, response, "location", getRedirectLocation(event));
98
+ setHeader(event, response, "content-type", "text/html; charset=utf-8");
99
+ appendSetCookie(event, response, serializeFormFailureCookie(failure));
100
+ }
@@ -0,0 +1 @@
1
+ export { holo, useHoloDb, useHoloDebug, useHoloEnv } from '../../composables'
@@ -1,6 +1,10 @@
1
- export {
2
- holo,
3
- useHoloDb,
4
- useHoloDebug,
5
- useHoloEnv
1
+ import {
2
+ holo as baseHolo,
3
+ useHoloDb as baseUseHoloDb,
4
+ useHoloDebug as baseUseHoloDebug,
5
+ useHoloEnv as baseUseHoloEnv
6
6
  } from "../../composables/index.js";
7
+ export const holo = baseHolo;
8
+ export const useHoloDb = baseUseHoloDb;
9
+ export const useHoloDebug = baseUseHoloDebug;
10
+ export const useHoloEnv = baseUseHoloEnv;
@@ -0,0 +1 @@
1
+ export { Storage, useStorage } from '../utils/storage'
@@ -0,0 +1,4 @@
1
+ export {
2
+ Storage,
3
+ useStorage
4
+ } from "../utils/storage.js";
@@ -1,4 +1,4 @@
1
- import { readFile, realpath } from "node:fs/promises";
1
+ import { open, realpath, stat } from "node:fs/promises";
2
2
  import { extname, resolve, sep } from "node:path";
3
3
  import { useRuntimeConfig } from "#imports";
4
4
  const NAMED_PUBLIC_DISK_ROUTE_SEGMENT = "__holo";
@@ -78,15 +78,28 @@ function createMissingFileError(message) {
78
78
  function isPathWithinRoot(root, absolutePath) {
79
79
  return absolutePath === root || absolutePath.startsWith(`${root}${sep}`);
80
80
  }
81
+ function isSameFile(left, right) {
82
+ return left.dev === right.dev && left.ino === right.ino;
83
+ }
81
84
  async function readPublicFile(event, disk, absolutePath) {
82
85
  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
+ const file = await open(absolutePath, "r");
87
+ try {
88
+ const openedFile = await file.stat();
89
+ const resolvedPath = await realpath(absolutePath);
90
+ if (!isPathWithinRoot(resolvedRoot, resolvedPath)) {
91
+ throw createMissingFileError("Storage file not found.");
92
+ }
93
+ const resolvedFile = await stat(resolvedPath);
94
+ if (!isSameFile(openedFile, resolvedFile)) {
95
+ throw createMissingFileError("Storage file not found.");
96
+ }
97
+ const contents = await file.readFile();
98
+ setResponseHeader(event, "content-type", resolveContentType(absolutePath));
99
+ return contents;
100
+ } finally {
101
+ await file.close();
86
102
  }
87
- const contents = await readFile(absolutePath);
88
- setResponseHeader(event, "content-type", resolveContentType(absolutePath));
89
- return contents;
90
103
  }
91
104
  function resolveRouteSegments(routePath) {
92
105
  const segments = normalizeRequestPath(routePath);
@@ -34,8 +34,15 @@ interface HoloRuntimeConfig extends RuntimeConfigInput {
34
34
  }
35
35
  }
36
36
 
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
+ */
37
43
  declare module '#app' {
38
44
  export function useRuntimeConfig(): HoloRuntimeConfig
45
+ export function useCookie<T = string | null>(name: string): { value: T | null | undefined }
39
46
  }
40
47
 
41
48
  declare module '#imports' {
@@ -43,8 +50,25 @@ declare module '#imports' {
43
50
  export function useStorage(base: string): unknown
44
51
  }
45
52
 
53
+ declare module 'nitropack/runtime/config' {
54
+ export function useRuntimeConfig(): HoloRuntimeConfig
55
+ }
56
+
57
+ declare module 'nitropack/runtime/context' {
58
+ export function useEvent(): unknown
59
+ }
60
+
61
+ declare module 'nitropack/runtime/plugin' {
62
+ export function defineNitroPlugin<T>(plugin: T): T
63
+ }
64
+
65
+ declare module 'nitropack/runtime/storage' {
66
+ export function useStorage(base: string): unknown
67
+ }
68
+
46
69
  declare global {
47
70
  function createError(input: { statusCode: number, statusMessage: string }): Error
71
+ function defineNitroErrorHandler<T>(handler: T): T
48
72
  function defineNitroPlugin<T>(plugin: T): T
49
73
  function defineEventHandler<T>(
50
74
  handler: (event: unknown) => T | Promise<T>,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holo-js/adapter-nuxt",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Holo-JS Framework - Nuxt adapter",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,15 +34,17 @@
34
34
  "test": "vitest --run"
35
35
  },
36
36
  "dependencies": {
37
- "@nuxt/kit": "^4.0.0",
38
- "@holo-js/config": "^0.1.3",
39
- "@holo-js/core": "^0.1.3",
40
- "@holo-js/db": "^0.1.3"
37
+ "@nuxt/kit": "^4.4.4",
38
+ "@holo-js/config": "^0.1.5",
39
+ "@holo-js/core": "^0.1.5",
40
+ "@holo-js/db": "^0.1.5",
41
+ "@holo-js/validation": "^0.1.5",
42
+ "h3": "^1.15.11"
41
43
  },
42
44
  "peerDependencies": {
43
- "@holo-js/forms": "^0.1.3",
44
- "@holo-js/storage": "^0.1.3",
45
- "@holo-js/storage-s3": "^0.1.3"
45
+ "@holo-js/forms": "^0.1.5",
46
+ "@holo-js/storage": "^0.1.5",
47
+ "@holo-js/storage-s3": "^0.1.5"
46
48
  },
47
49
  "peerDependenciesMeta": {
48
50
  "@holo-js/forms": {
@@ -56,11 +58,11 @@
56
58
  }
57
59
  },
58
60
  "devDependencies": {
59
- "@holo-js/storage-s3": "workspace:*",
61
+ "@holo-js/storage-s3": "^0.1.5",
60
62
  "@nuxt/module-builder": "^1.0.2",
61
63
  "@types/node": "^22.10.2",
62
- "nuxt": "^4.0.0",
64
+ "nuxt": "^4.4.4",
63
65
  "typescript": "^5.7.2",
64
- "vitest": "^2.1.8"
66
+ "vitest": "^4.1.5"
65
67
  }
66
68
  }