nuxt-directus-sdk 5.0.1 → 6.0.0-beta.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/README.md CHANGED
@@ -70,10 +70,16 @@ For cross-domain setups (e.g., `app.example.com` ↔ `api.example.com`), see the
70
70
 
71
71
  ## Development
72
72
 
73
+ > [!IMPORTANT] The playground uses [directus-template-cli](https://github.com/directus-labs/directus-template-cli?tab=readme-ov-file#applying-a-template) `cms` template.
74
+ > Apply the template with `npx directus-template-cli@latest apply` and follow the interactive prompts.
75
+
73
76
  ```bash
74
77
  # Install dependencies
75
78
  bun install
76
79
 
80
+ # Add DIRECTUS_ADMIN_TOKEN to playground .env (don't forget to update your token)
81
+ cp ./playground/.env.example ./playground/.env
82
+
77
83
  # Generate type stubs
78
84
  bun run dev:prepare
79
85
 
@@ -3,7 +3,7 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import { parseArgs } from 'node:util';
5
5
  import { createDirectus, staticToken, rest } from '@directus/sdk';
6
- import { e as diffRemoteRules, j as formatDiff, d as compareRulesPayloads, g as fetchRemoteRules, b as loadRulesFromPayload, m as pushRules, k as formatPushResult, h as fetchRemoteRulesAsJson } from '../shared/nuxt-directus-sdk.VgzMwq7f.mjs';
6
+ import { d as diffRemoteRules, e as formatDiff, c as compareRulesPayloads, f as fetchRemoteRules, k as loadRulesFromPayload, o as pushRules, g as formatPushResult, b as fetchRemoteRulesAsJson } from '../shared/nuxt-directus-sdk.1qEbZAZ_.mjs';
7
7
 
8
8
  function loadEnv() {
9
9
  const envPath = resolve(process.cwd(), ".env");
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-directus-sdk",
3
- "version": "5.0.1",
3
+ "version": "6.0.0-beta.0",
4
4
  "configKey": "directus",
5
5
  "compatibility": {
6
6
  "nuxt": "^4.0.0"
package/dist/module.mjs CHANGED
@@ -1,12 +1,12 @@
1
- import { useLogger, defineNuxtModule, createResolver, addServerHandler, hasNuxtModule, tryResolveModule, addPlugin, addRouteMiddleware, addImportsDir, addComponentsDir, addImportsSources, addTypeTemplate, installModule } from '@nuxt/kit';
1
+ import { useLogger, defineNuxtModule, createResolver, addServerHandler, hasNuxtModule, tryResolveModule, addPlugin, addComponentsDir, addRouteMiddleware, addImportsDir, addImportsSources, addTypeTemplate, installModule } from '@nuxt/kit';
2
2
  import { colors } from 'consola/utils';
3
3
  import { defu } from 'defu';
4
4
  import { joinURL } from 'ufo';
5
- import { generateTypes } from '../dist/runtime/types/index.js';
5
+ import { generateTypesFromDirectus } from '../dist/runtime/types/index.js';
6
6
  import { useUrl } from '../dist/runtime/utils/index.js';
7
7
 
8
8
  const name = "nuxt-directus-sdk";
9
- const version = "5.0.1";
9
+ const version = "6.0.0-beta.0";
10
10
 
11
11
  const configKey = "directus";
12
12
  const logger = useLogger("nuxt-directus-sdk");
@@ -59,17 +59,17 @@ const module$1 = defineNuxtModule({
59
59
  nuxtApp.options[key] = defu(nuxtApp.options[key], moduleOptions);
60
60
  }
61
61
  }
62
+ const loggerMessage = [];
62
63
  const devProxyConfig = typeof options.devProxy === "boolean" ? { enabled: options.devProxy } : { ...options.devProxy };
63
64
  const directusUrl = serverUrl || clientUrl;
64
65
  const devProxyEnabled = devProxyConfig.enabled ?? nuxtApp.options.dev;
65
66
  const devProxyPath = devProxyConfig.path ?? "/directus";
66
67
  const wsProxyPath = devProxyConfig.wsPath ?? `${devProxyPath}-ws`;
67
68
  const wsTarget = joinURL(directusUrl, "websocket");
68
- const loggerMessage = [];
69
69
  if (devProxyEnabled && nuxtApp.options.dev) {
70
- loggerMessage.push(`\u{1F310} Development mode:`);
71
- loggerMessage.push(`URL${colors.dim(` ${devProxyPath}`)} proxies ${colors.underline(colors.green(`${directusUrl}`))}`);
72
- loggerMessage.push(`WS URL${colors.dim(` ${wsProxyPath}`)} proxies ${colors.underline(colors.green(`${wsTarget}`))}`);
70
+ loggerMessage.push(`\u{1F310} Development Proxy Mode Enabled:`);
71
+ loggerMessage.push(` - URL${colors.dim(` ${devProxyPath}`)} proxies ${colors.underline(colors.green(`${directusUrl}`))}`);
72
+ loggerMessage.push(` - WS URL${colors.dim(` ${wsProxyPath}`)} proxies ${colors.underline(colors.green(`${wsTarget}`))}`, "");
73
73
  nuxtApp.options.nitro = nuxtApp.options.nitro || {};
74
74
  nuxtApp.options.nitro.devProxy = nuxtApp.options.nitro.devProxy || {};
75
75
  nuxtApp.options.nitro.devProxy[wsProxyPath] = {
@@ -128,7 +128,7 @@ const module$1 = defineNuxtModule({
128
128
  wsPath: wsProxyPath
129
129
  };
130
130
  } else if (!nuxtApp.options.dev) {
131
- loggerMessage.push(`\u{1F310} Production mode:`, ` SDK connects directly to ${colors.dim(`${directusUrl}`)}`);
131
+ loggerMessage.push(`\u{1F310} Production Mode:`, ` - SDK connects directly to ${colors.dim(`${directusUrl}`)}`, "");
132
132
  options.devProxy = false;
133
133
  }
134
134
  options.directusUrl = clientUrl;
@@ -153,11 +153,22 @@ const module$1 = defineNuxtModule({
153
153
  modifiers
154
154
  }
155
155
  });
156
+ loggerMessage.push("\u{1F4F7} Nuxt/Image default provider is set to Directus", "");
156
157
  }
157
158
  addPlugin(resolver.resolve("./runtime/plugin"));
158
159
  const hasVisualEditing = options.visualEditor && await tryResolveModule("@directus/visual-editing", new URL(import.meta.url));
159
160
  if (hasVisualEditing) {
160
161
  addPlugin(resolver.resolve("./runtime/plugins/visual-editor.client"));
162
+ addComponentsDir({
163
+ path: resolver.resolve("./runtime/components"),
164
+ pathPrefix: false,
165
+ prefix: "",
166
+ global: true
167
+ });
168
+ loggerMessage.push("\u{1F4DD} Visual Editor Components Added", "");
169
+ }
170
+ if (options.auth?.enableGlobalAuthMiddleware) {
171
+ loggerMessage.push("\u{1F512} Auth middleware installed globally.", "");
161
172
  }
162
173
  addRouteMiddleware({
163
174
  name: "auth",
@@ -169,14 +180,6 @@ const module$1 = defineNuxtModule({
169
180
  path: resolver.resolve("./runtime/middleware/guest")
170
181
  });
171
182
  addImportsDir(resolver.resolve("./runtime/composables"));
172
- if (hasVisualEditing) {
173
- addComponentsDir({
174
- path: resolver.resolve("./runtime/components"),
175
- pathPrefix: false,
176
- prefix: "",
177
- global: true
178
- });
179
- }
180
183
  const directusSdkImports = {
181
184
  from: "@directus/sdk",
182
185
  imports: [
@@ -251,9 +254,8 @@ const module$1 = defineNuxtModule({
251
254
  ]
252
255
  });
253
256
  });
254
- loggerMessage.push(``);
255
257
  if (options.devtools) {
256
- loggerMessage.push(`Directus Admin added to Nuxt DevTools`);
258
+ loggerMessage.push(`\u{1F4E6} Directus added to Nuxt DevTools`, "");
257
259
  nuxtApp.hook("devtools:customTabs", (iframeTabs) => {
258
260
  iframeTabs.push({
259
261
  name: "directus",
@@ -265,33 +267,29 @@ const module$1 = defineNuxtModule({
265
267
  }
266
268
  });
267
269
  });
268
- } else {
269
- loggerMessage.push(`${colors.dim(` Directus Admin was not added to Nuxt DevTools`)}`);
270
270
  }
271
271
  const typesEnabled = typeof options.types === "boolean" && options.types || options.types && options.types.enabled === true;
272
272
  const typesPrefix = typeof options.types === "object" ? options.types.prefix ?? "" : "";
273
273
  if (typesEnabled) {
274
+ loggerMessage.push("\u{1F4CB} Directus Type Generator Enabled");
274
275
  if (!options.adminToken) {
275
- loggerMessage.push(``, `${colors.bgRedBright(`${colors.red("\u2691 ERROR:")} Unable to generate Types`)}`, ` Fix: Set adminToken in config or DIRECTUS_ADMIN_TOKEN in .env`);
276
+ loggerMessage.push(` ${colors.bgRedBright(`${colors.red("\u2691 ERROR:")} Unable to generate Types`)}`, ` Fix: Set adminToken in config or DIRECTUS_ADMIN_TOKEN in .env`);
276
277
  } else {
277
278
  try {
278
- let cachedTypes = null;
279
+ const { typeString, logs } = await generateTypesFromDirectus(directusUrl, options.adminToken, typesPrefix);
280
+ loggerMessage.push(...logs);
279
281
  addTypeTemplate({
280
282
  filename: `types/${configKey}.d.ts`,
281
- async getContents() {
282
- if (!cachedTypes) {
283
- cachedTypes = await generateTypes({
284
- url: directusUrl,
285
- token: options.adminToken,
286
- prefix: typesPrefix
287
- });
288
- }
289
- return cachedTypes;
283
+ getContents() {
284
+ return typeString;
290
285
  }
291
286
  }, { nitro: true, nuxt: true });
292
- loggerMessage.push(`${colors.dim(` Directus Types saved successfully to #build/types/${configKey}.d.ts`)}`);
287
+ if (logs.some((log) => log.toLowerCase().includes("error"))) {
288
+ throw new Error(` ${colors.bgRedBright(`${colors.red("\u2691 ERROR:")} TypeGenerator returned an error`)}`);
289
+ }
290
+ loggerMessage.push(` - Directus Types saved successfully to ${colors.dim(`#build/types/${configKey}.d.ts`)}`);
293
291
  } catch (error) {
294
- logger.error(error.message);
292
+ loggerMessage.push(`${error instanceof Error ? error.message : String(error)}`, ` - Fallback DirectusSchema is being used ${colors.dim("(not recommended)")}`);
295
293
  }
296
294
  }
297
295
  }
@@ -1,5 +1,5 @@
1
- import { i as isStandardSchema } from '../shared/nuxt-directus-sdk.VgzMwq7f.mjs';
2
- export { d as compareRulesPayloads, e as diffRemoteRules, f as diffRules, g as fetchRemoteRules, h as fetchRemoteRulesAsJson, j as formatDiff, k as formatPushResult, q as isValidationStandardSchema, l as loadRulesFromJson, a as loadRulesFromJsonFile, b as loadRulesFromPayload, c as loadRulesFromPayloadFile, n as normalizeRules, p as pullRules, m as pushRules, r as rulesToJson, s as serializeToDirectusApi, o as serializeToJson, t as toDirectusValidation } from '../shared/nuxt-directus-sdk.VgzMwq7f.mjs';
1
+ import { i as isStandardSchema } from '../shared/nuxt-directus-sdk.1qEbZAZ_.mjs';
2
+ export { c as compareRulesPayloads, d as diffRemoteRules, a as diffRules, f as fetchRemoteRules, b as fetchRemoteRulesAsJson, e as formatDiff, g as formatPushResult, h as isValidationStandardSchema, l as loadRulesFromJson, j as loadRulesFromJsonFile, k as loadRulesFromPayload, m as loadRulesFromPayloadFile, n as normalizeRules, p as pullRules, o as pushRules, r as rulesToJson, s as serializeToDirectusApi, q as serializeToJson, t as toDirectusValidation } from '../shared/nuxt-directus-sdk.1qEbZAZ_.mjs';
3
3
  import '@directus/sdk';
4
4
 
5
5
  function isPolicyReference(input) {
@@ -1,10 +1,10 @@
1
1
  import type { PrimaryKey } from '@directus/types';
2
2
  declare const __VLS_export: <T extends keyof DirectusSchema>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
3
- props: __VLS_PrettifyLocal<{
3
+ props: import("vue").PublicProps & __VLS_PrettifyLocal<{
4
4
  collection: T;
5
5
  item: PrimaryKey;
6
6
  mode?: "drawer" | "modal" | "popover";
7
- }> & import("vue").PublicProps & (typeof globalThis extends {
7
+ }> & (typeof globalThis extends {
8
8
  __VLS_PROPS_FALLBACK: infer P;
9
9
  } ? P : {});
10
10
  expose: (exposed: {}) => void;
@@ -1,10 +1,10 @@
1
1
  import type { PrimaryKey } from '@directus/types';
2
2
  declare const __VLS_export: <T extends keyof DirectusSchema>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
3
- props: __VLS_PrettifyLocal<{
3
+ props: import("vue").PublicProps & __VLS_PrettifyLocal<{
4
4
  collection: T;
5
5
  item: PrimaryKey;
6
6
  mode?: "drawer" | "modal" | "popover";
7
- }> & import("vue").PublicProps & (typeof globalThis extends {
7
+ }> & (typeof globalThis extends {
8
8
  __VLS_PROPS_FALLBACK: infer P;
9
9
  } ? P : {});
10
10
  expose: (exposed: {}) => void;
@@ -1,11 +1,11 @@
1
1
  import type { PrimaryKey } from '@directus/types';
2
2
  declare const __VLS_export: <T extends keyof DirectusSchema>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
3
- props: __VLS_PrettifyLocal<{
3
+ props: import("vue").PublicProps & __VLS_PrettifyLocal<{
4
4
  collection: T;
5
5
  item: PrimaryKey;
6
6
  fields?: (string | number | symbol) | (string | number | symbol)[];
7
7
  mode?: "drawer" | "modal" | "popover";
8
- }> & import("vue").PublicProps & (typeof globalThis extends {
8
+ }> & (typeof globalThis extends {
9
9
  __VLS_PROPS_FALLBACK: infer P;
10
10
  } ? P : {});
11
11
  expose: (exposed: {}) => void;
@@ -1,11 +1,11 @@
1
1
  import type { PrimaryKey } from '@directus/types';
2
2
  declare const __VLS_export: <T extends keyof DirectusSchema>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
3
- props: __VLS_PrettifyLocal<{
3
+ props: import("vue").PublicProps & __VLS_PrettifyLocal<{
4
4
  collection: T;
5
5
  item: PrimaryKey;
6
6
  fields?: (string | number | symbol) | (string | number | symbol)[];
7
7
  mode?: "drawer" | "modal" | "popover";
8
- }> & import("vue").PublicProps & (typeof globalThis extends {
8
+ }> & (typeof globalThis extends {
9
9
  __VLS_PROPS_FALLBACK: infer P;
10
10
  } ? P : {});
11
11
  expose: (exposed: {}) => void;
@@ -1,2 +1,2 @@
1
- declare const _default: import("#app").RouteMiddleware;
1
+ declare const _default: import("nuxt/app").RouteMiddleware;
2
2
  export default _default;
@@ -1,2 +1,2 @@
1
- declare const _default: import("#app").RouteMiddleware;
1
+ declare const _default: import("nuxt/app").RouteMiddleware;
2
2
  export default _default;
@@ -1,2 +1,2 @@
1
- declare const _default: import("#app").Plugin<Record<string, unknown>> & import("#app").ObjectPlugin<Record<string, unknown>>;
1
+ declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
2
2
  export default _default;
@@ -1,8 +1,8 @@
1
- declare const _default: import("#app").Plugin<{
1
+ declare const _default: import("nuxt/app").Plugin<{
2
2
  directusVisualEditing: {
3
3
  refresh: () => Promise<void>;
4
4
  };
5
- }> & import("#app").ObjectPlugin<{
5
+ }> & import("nuxt/app").ObjectPlugin<{
6
6
  directusVisualEditing: {
7
7
  refresh: () => Promise<void>;
8
8
  };
@@ -0,0 +1,15 @@
1
+ import type { SnapshotField } from '@directus/types';
2
+ export interface TypegenExtension {
3
+ name: (prefix?: string) => string;
4
+ isMatch: (field: SnapshotField) => boolean;
5
+ output: (prefix: string | undefined) => string;
6
+ }
7
+ export declare const typegenExtensions: TypegenExtension[];
8
+ /**
9
+ * Resolve the first matching extension or false if no extension matches
10
+ *
11
+ */
12
+ export declare function resolveTypegenExtension(field: SnapshotField, prefix?: unknown): {
13
+ name: string;
14
+ output: string;
15
+ } | false;
@@ -0,0 +1,15 @@
1
+ import { extension as seoPlugin } from "./seo-plugin.js";
2
+ export const typegenExtensions = [
3
+ seoPlugin
4
+ ];
5
+ export function resolveTypegenExtension(field, prefix) {
6
+ const safePrefix = typeof prefix === "string" ? prefix : "";
7
+ const match = typegenExtensions.find((ext) => ext.isMatch(field));
8
+ if (!match) {
9
+ return false;
10
+ }
11
+ return {
12
+ name: match.name(safePrefix),
13
+ output: match.output(safePrefix)
14
+ };
15
+ }
@@ -0,0 +1,2 @@
1
+ import type { TypegenExtension } from './index.ts.js';
2
+ export declare const extension: TypegenExtension;
@@ -0,0 +1,24 @@
1
+ const interfaceName = "DirectusLabsSeoPlugin";
2
+ export const extension = {
3
+ name(prefix) {
4
+ return `${prefix}${interfaceName}`;
5
+ },
6
+ isMatch(field) {
7
+ return field.type === "json" && field.meta?.interface === "seo-interface" && field.meta?.special?.includes("cast-json") === true;
8
+ },
9
+ output(prefix) {
10
+ return `
11
+ interface ${prefix}${interfaceName} {
12
+ title?: string;
13
+ meta_description?: string;
14
+ og_image?: string;
15
+ additional_fields?: Record<string, unknown>;
16
+ sitemap?: {
17
+ change_frequency: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
18
+ priority: string;
19
+ };
20
+ no_index?: boolean;
21
+ no_follow?: boolean;
22
+ }`;
23
+ }
24
+ };
@@ -1,6 +1,34 @@
1
- import type { GenerateOptions } from './types.js';
1
+ import type { SnapshotCollection, SnapshotField, SnapshotRelation } from '@directus/types';
2
+ import type { TypegenExtension } from './extensions/index.js';
3
+ export declare const FALLBACK_TYPE_STRING = "declare global {\n\ninterface DirectusFile {\n\tid: string;\n}\ninterface DirectusUser {\n\tid: string;\n}\ninterface DirectusSchema { }\n}\n\nexport {};";
2
4
  /**
3
- * Generate TypeScript types from Directus collections using directus-sdk-typegen
4
- * Wraps the types in a declare global block for Nuxt compatibility
5
+ * Fetches collections, fields, and relations from a live Directus instance and
6
+ * generates a TypeScript declaration string.
7
+ *
8
+ * @param url - Base URL of the Directus instance.
9
+ * @param token - Static access token for authentication.
10
+ * @param prefix - Prefix used when generating interface names.
11
+ * @returns The generated TypeScript string and an array of log messages.
5
12
  */
6
- export declare function generateTypes(options: GenerateOptions): Promise<string>;
13
+ export declare function generateTypesFromDirectus(url: string, token: string, prefix: string): Promise<{
14
+ typeString: string;
15
+ logs: string[];
16
+ }>;
17
+ /**
18
+ * Transforms Directus collections, fields, and relations into a TypeScript declaration string.
19
+ *
20
+ * - Builds a relation map from relations.
21
+ * - Separates custom and system collections and infers missing system ones.
22
+ * - Tracks singleton collections.
23
+ * - Generates collection interfaces and deduplicated extension outputs.
24
+ * - Generates `DirectusSchema` and `CollectionNames` enum.
25
+ * - Wraps everything in a `declare global` block.
26
+ *
27
+ * @param collections - Collection definitions from the Directus snapshot.
28
+ * @param fields - Field definitions from the Directus snapshot.
29
+ * @param relations - Relation definitions from the Directus snapshot.
30
+ * @param prefix - Prefix used for generating interface names.
31
+ * @param extensions - Optional type generation extensions.
32
+ * @returns A TypeScript declaration file as a string.
33
+ */
34
+ export declare function transformSnapshotToTypeString(collections: SnapshotCollection[], fields: SnapshotField[], relations: SnapshotRelation[], prefix: string, extensions?: TypegenExtension[]): string;
@@ -1,44 +1,428 @@
1
- import { useLogger } from "@nuxt/kit";
2
- import { generateDirectusTypes } from "directus-sdk-typegen";
3
- import { cleanDoubleSlashes, withoutTrailingSlash } from "ufo";
4
- const logger = useLogger("nuxt-directus-sdk");
5
- export async function generateTypes(options) {
6
- logger.info(`Generating types from: ${options.url}`);
7
- const generatedTypes = await generateDirectusTypes({
8
- directusUrl: withoutTrailingSlash(cleanDoubleSlashes(options.url)),
9
- directusToken: options.token
1
+ import { createDirectus, isDirectusError, readCollections, readFields, readRelations, rest, staticToken } from "@directus/sdk";
2
+ import { typegenExtensions } from "./extensions/index.js";
3
+ export const FALLBACK_TYPE_STRING = "declare global {\n\ninterface DirectusFile {\n id: string;\n}\ninterface DirectusUser {\n id: string;\n}\ninterface DirectusSchema { }\n}\n\nexport {};";
4
+ export async function generateTypesFromDirectus(url, token, prefix) {
5
+ const logs = [];
6
+ const client = createDirectus(url).with(rest()).with(staticToken(token));
7
+ let result = null;
8
+ try {
9
+ const [collections, fields, relations] = await Promise.all([
10
+ client.request(readCollections()),
11
+ client.request(readFields()),
12
+ client.request(readRelations())
13
+ ]);
14
+ logs.push(` - Fetched ${collections.length} collections, ${fields.length} fields, ${relations.length} relations`);
15
+ if (collections.length === 0 || fields.length === 0 || relations.length === 0) {
16
+ throw new Error(`Empty response from Directus \u2014 collections: ${collections.length}, fields: ${fields.length}, relations: ${relations.length}`);
17
+ }
18
+ result = [
19
+ collections,
20
+ fields,
21
+ relations
22
+ ];
23
+ } catch (error) {
24
+ if (isDirectusError(error)) {
25
+ logs.push(` - Directus error ${error.errors.map((e) => `[${e.extensions?.code}] ${e.message}`).join(", ")}`);
26
+ } else {
27
+ logs.push(` - Error: ${error instanceof Error ? error.message : String(error)}`);
28
+ }
29
+ return { typeString: FALLBACK_TYPE_STRING, logs };
30
+ }
31
+ const typeString = transformSnapshotToTypeString(...result, prefix);
32
+ return { typeString, logs };
33
+ }
34
+ export function transformSnapshotToTypeString(collections, fields, relations, prefix, extensions = typegenExtensions) {
35
+ const relationMap = buildRelationMapFromSnapshot(relations);
36
+ const collectionsWithDatabaseTables = collections.filter(
37
+ (c) => c.schema !== null
38
+ );
39
+ const customCollections = collectionsWithDatabaseTables.filter(
40
+ (c) => !collectionIsDirectusSystem(c.collection)
41
+ );
42
+ const systemCollectionsFromSnapshot = collectionsWithDatabaseTables.filter(
43
+ (c) => collectionIsDirectusSystem(c.collection)
44
+ );
45
+ const systemCollectionNamesAlreadyPresent = new Set(
46
+ systemCollectionsFromSnapshot.map((c) => c.collection)
47
+ );
48
+ const impliedSystemCollections = [
49
+ ...new Set(
50
+ relations.flatMap((r) => [r.collection, r.related_collection ?? ""]).filter(
51
+ (name) => collectionIsDirectusSystem(name) && !systemCollectionNamesAlreadyPresent.has(name)
52
+ )
53
+ )
54
+ ].map((name) => ({ collection: name, schema: { name }, meta: null }));
55
+ const allCollectionsForSchema = [
56
+ ...collectionsWithDatabaseTables,
57
+ ...impliedSystemCollections
58
+ ];
59
+ const singletonCollectionNames = new Set(
60
+ allCollectionsForSchema.filter((c) => c.meta?.singleton === true).map((c) => c.collection)
61
+ );
62
+ const generatedCollections = [
63
+ ...customCollections,
64
+ ...systemCollectionsFromSnapshot
65
+ ].map(
66
+ (collection) => generateInterfaceForCollection(collection, fields, relationMap, prefix, extensions, singletonCollectionNames)
67
+ );
68
+ const seenExtensionOutputs = /* @__PURE__ */ new Set();
69
+ const uniqueExtensionOutputs = generatedCollections.flatMap((g) => g.extensionOutputs).filter((output) => {
70
+ if (seenExtensionOutputs.has(output))
71
+ return false;
72
+ seenExtensionOutputs.add(output);
73
+ return true;
74
+ });
75
+ const customInterfaceBlocks = generatedCollections.map((g) => g.interfaceBlock);
76
+ const directusSchemaBlock = generateDirectusSchemaInterface(allCollectionsForSchema, prefix, singletonCollectionNames);
77
+ const allCollectionNames = allCollectionsForSchema.map((c) => c.collection);
78
+ const enumBlock = generateCollectionNamesEnum(allCollectionNames, prefix);
79
+ const bodyParts = [
80
+ ...uniqueExtensionOutputs,
81
+ ...customInterfaceBlocks,
82
+ directusSchemaBlock,
83
+ enumBlock
84
+ ];
85
+ return [
86
+ "declare global {",
87
+ "",
88
+ bodyParts.join("\n\n"),
89
+ "}",
90
+ "",
91
+ "export {};"
92
+ ].join("\n");
93
+ }
94
+ function collectionIsDirectusSystem(collectionName) {
95
+ return collectionName.startsWith("directus_");
96
+ }
97
+ function collectionNameToInterfaceName(collectionName, prefix, singletons = /* @__PURE__ */ new Set()) {
98
+ const isSingleton = singletons.has(collectionName);
99
+ const transform = (name) => pascalCase(isSingleton ? name : singularize(name));
100
+ if (collectionIsDirectusSystem(collectionName)) {
101
+ return transform(collectionName);
102
+ }
103
+ return `${prefix}${transform(collectionName)}`;
104
+ }
105
+ function resolveExtensionForField(field, prefix, extensions) {
106
+ const match = extensions.find((ext) => ext.isMatch(field));
107
+ if (!match)
108
+ return null;
109
+ return {
110
+ name: match.name(prefix),
111
+ output: match.output(prefix)
112
+ };
113
+ }
114
+ function buildRelationMapFromSnapshot(relations) {
115
+ const relationMap = /* @__PURE__ */ new Map();
116
+ const getOrCreateCollectionRelations = (collection) => {
117
+ if (!relationMap.has(collection)) {
118
+ relationMap.set(collection, { m2o: /* @__PURE__ */ new Map(), o2m: /* @__PURE__ */ new Map(), m2a: /* @__PURE__ */ new Map() });
119
+ }
120
+ return relationMap.get(collection);
121
+ };
122
+ for (const relation of relations) {
123
+ const { collection, field, related_collection, meta } = relation;
124
+ if (!meta)
125
+ continue;
126
+ const isM2A = meta.one_allowed_collections && meta.one_allowed_collections.length > 0;
127
+ if (isM2A) {
128
+ getOrCreateCollectionRelations(collection).m2a.set(field, meta.one_allowed_collections);
129
+ } else if (related_collection) {
130
+ getOrCreateCollectionRelations(collection).m2o.set(field, { relatedCollection: related_collection });
131
+ if (meta.one_field) {
132
+ getOrCreateCollectionRelations(related_collection).o2m.set(meta.one_field, { relatedCollection: collection });
133
+ }
134
+ }
135
+ }
136
+ return relationMap;
137
+ }
138
+ function resolveFieldTypeString(snapshotField, collectionRelations, prefix, extensions, singletons = /* @__PURE__ */ new Set()) {
139
+ if (collectionRelations?.m2a.has(snapshotField.field)) {
140
+ const allowedCollections = collectionRelations.m2a.get(snapshotField.field);
141
+ const unionTypes = allowedCollections.map((c) => collectionNameToInterfaceName(c, prefix, singletons)).join(" | ");
142
+ return { tsType: `${unionTypes} | string`, extensionOutput: null };
143
+ }
144
+ if (collectionRelations?.m2o.has(snapshotField.field)) {
145
+ const related = collectionRelations.m2o.get(snapshotField.field);
146
+ return { tsType: `${collectionNameToInterfaceName(related.relatedCollection, prefix, singletons)} | string`, extensionOutput: null };
147
+ }
148
+ if (collectionRelations?.o2m.has(snapshotField.field)) {
149
+ const related = collectionRelations.o2m.get(snapshotField.field);
150
+ return { tsType: `${collectionNameToInterfaceName(related.relatedCollection, prefix, singletons)}[] | string[]`, extensionOutput: null };
151
+ }
152
+ const extensionMatch = resolveExtensionForField(snapshotField, prefix, extensions);
153
+ if (extensionMatch) {
154
+ return { tsType: extensionMatch.name, extensionOutput: extensionMatch.output };
155
+ }
156
+ return { tsType: determineFieldType(snapshotField), extensionOutput: null };
157
+ }
158
+ const ALIAS_SPECIAL_TYPES = /* @__PURE__ */ new Set(["alias", "no-data", "group"]);
159
+ function fieldIsUiOnlyAlias(field) {
160
+ const special = field.meta?.special ?? [];
161
+ return field.type === "alias" && special.some((s) => ALIAS_SPECIAL_TYPES.has(s)) && !special.includes("o2m") && !special.includes("m2o") && !special.includes("m2a") && !special.includes("files") && !special.includes("file");
162
+ }
163
+ function buildInterfaceField(snapshotField, collectionRelations, prefix, extensions, singletons = /* @__PURE__ */ new Set()) {
164
+ if (fieldIsUiOnlyAlias(snapshotField))
165
+ return null;
166
+ const isPrimaryKey = snapshotField.schema?.is_primary_key === true;
167
+ const isRequired = snapshotField.meta?.required === true;
168
+ const isNullable = snapshotField.schema?.is_nullable !== false;
169
+ const { tsType, extensionOutput } = resolveFieldTypeString(snapshotField, collectionRelations, prefix, extensions, singletons);
170
+ const shouldAppendNull = isNullable && !isRequired && !isPrimaryKey;
171
+ const finalType = shouldAppendNull ? `${tsType} | null` : tsType;
172
+ return {
173
+ interfaceField: {
174
+ fieldName: snapshotField.field,
175
+ typeString: finalType,
176
+ isOptional: !isPrimaryKey && !isRequired,
177
+ snapshotField,
178
+ sortOrder: snapshotField.meta?.sort ?? 9999
179
+ },
180
+ extensionOutput
181
+ };
182
+ }
183
+ function generateInterfaceForCollection(collection, allFields, relationMap, prefix, extensions, singletons = /* @__PURE__ */ new Set()) {
184
+ const collectionName = collection.collection;
185
+ const interfaceName = collectionNameToInterfaceName(collectionName, prefix, singletons);
186
+ const collectionRelations = relationMap.get(collectionName);
187
+ const builtFields = allFields.filter((f) => f.collection === collectionName).map((f) => buildInterfaceField(f, collectionRelations, prefix, extensions, singletons)).filter((f) => f !== null).sort((a, b) => a.interfaceField.sortOrder - b.interfaceField.sortOrder);
188
+ const extensionOutputs = builtFields.map((f) => f.extensionOutput).filter((o) => o !== null);
189
+ const fieldLines = builtFields.map(({ interfaceField }) => {
190
+ const jsDoc = generateJSDocComment(interfaceField.snapshotField);
191
+ const optionalMarker = interfaceField.isOptional ? "?" : "";
192
+ const declaration = ` ${interfaceField.fieldName}${optionalMarker}: ${interfaceField.typeString};`;
193
+ return jsDoc ? `${jsDoc} ${interfaceField.fieldName}${optionalMarker}: ${interfaceField.typeString};` : declaration;
194
+ });
195
+ return {
196
+ interfaceBlock: `interface ${interfaceName} {
197
+ ${fieldLines.join("\n")}
198
+ }`,
199
+ extensionOutputs
200
+ };
201
+ }
202
+ function generateDirectusSchemaInterface(allCollections, prefix, singletons = /* @__PURE__ */ new Set()) {
203
+ const entries = allCollections.map((collection) => {
204
+ const isSingleton = collection.meta?.singleton === true;
205
+ const interfaceName = collectionNameToInterfaceName(collection.collection, prefix, singletons);
206
+ const valueType = isSingleton ? interfaceName : `${interfaceName}[]`;
207
+ return ` ${collection.collection}: ${valueType};`;
10
208
  });
11
- let processedTypes = generatedTypes;
12
- if (options.prefix) {
13
- processedTypes = processedTypes.replace(
14
- /^export interface ((?!Schema\b)(?!Directus)\w+)/gm,
15
- (match, interfaceName) => {
16
- if (interfaceName.startsWith("Directus")) {
17
- return match;
209
+ return `interface DirectusSchema {
210
+ ${entries.join("\n")}
211
+ }`;
212
+ }
213
+ function generateCollectionNamesEnum(collectionNames, prefix) {
214
+ const entries = collectionNames.map((name) => ` ${name} = '${name}'`);
215
+ return `export enum ${prefix}CollectionNames {
216
+ ${entries.join(",\n")}
217
+ }`;
218
+ }
219
+ function determineFieldType(field) {
220
+ if (field.meta?.special?.includes("translations")) {
221
+ const translationsCollection = field.relation?.collection;
222
+ if (translationsCollection) {
223
+ const translationType = pascalCase(singularize(translationsCollection));
224
+ return `${translationType}[] | null`;
225
+ }
226
+ }
227
+ if (field.relation?.collection) {
228
+ const relatedTypeName = pascalCase(singularize(field.relation.collection));
229
+ switch (field.relation.type) {
230
+ case "many":
231
+ return `${relatedTypeName}[] | string[]`;
232
+ case "m2a": {
233
+ const allowedCollections = field.relation.allowedCollections;
234
+ if (Array.isArray(allowedCollections) && allowedCollections.length > 0) {
235
+ const unionTypes = allowedCollections.map((collection) => pascalCase(singularize(collection))).join(" | ");
236
+ return `${unionTypes} | string`;
18
237
  }
19
- return `export interface ${options.prefix}${interfaceName}`;
238
+ console.warn(
239
+ "[determineFieldType] m2a relation missing allowedCollections. Falling back to string."
240
+ );
241
+ return "string";
20
242
  }
243
+ default:
244
+ return `${relatedTypeName} | string`;
245
+ }
246
+ }
247
+ const choices = field.meta?.options?.choices;
248
+ if (Array.isArray(choices) && choices.length > 0) {
249
+ const choiceValues = choices.map(
250
+ (choice) => choice.value === null ? "null" : escapeStringLiteral(choice.value)
21
251
  );
22
- processedTypes = processedTypes.split("\n").map((line) => {
23
- if (line.match(/^export interface /)) {
24
- return line;
252
+ const unionOfChoices = [...new Set(choiceValues)].join(" | ");
253
+ const interfacesWithMultiSelect = ["select-multiple", "select-multiple-dropdown", "select-multiple-checkbox"];
254
+ if (interfacesWithMultiSelect.includes(field.meta.interface)) {
255
+ return `Array<${unionOfChoices}>`;
256
+ }
257
+ return unionOfChoices;
258
+ }
259
+ switch (field.type) {
260
+ case "boolean":
261
+ return "boolean";
262
+ case "json": {
263
+ const nestedFields = field.meta?.options?.fields;
264
+ if (Array.isArray(nestedFields) && nestedFields.length > 0) {
265
+ const nestedTypes = nestedFields.map((nestedField) => {
266
+ const propertyName = nestedField.field ?? nestedField.name;
267
+ const fieldType = determineFieldType(nestedField);
268
+ return `${propertyName}: ${fieldType}`;
269
+ });
270
+ return `Array<{ ${nestedTypes.join("; ")} }>`;
25
271
  }
26
- return line.replace(
27
- /\b(?!Directus)([A-Z]\w+)(\[\])?/g,
28
- (match, typeName, array) => {
29
- if (typeName.startsWith("Directus") || ["String", "Number", "Boolean", "Date", "Array", "Record", "Promise", "Partial", "Required", "Readonly", "Pick", "Omit", "Exclude", "Extract"].includes(typeName) || ["string", "number", "boolean", "any", "unknown", "void", "never", "null", "undefined"].includes(typeName.toLowerCase())) {
30
- return match;
31
- }
32
- return `${options.prefix}${typeName}${array || ""}`;
33
- }
34
- );
35
- }).join("\n");
272
+ if (field.meta?.interface === "input-code") {
273
+ return "Record<string, unknown>";
274
+ }
275
+ if (field.meta?.interface === "tags") {
276
+ return "string[]";
277
+ }
278
+ return "'json'";
279
+ }
280
+ case "csv":
281
+ return "'csv'";
282
+ case "dateTime":
283
+ case "timestamp":
284
+ return "'datetime'";
285
+ case "date":
286
+ return "'date'";
287
+ case "time":
288
+ return "'time'";
289
+ case "integer":
290
+ case "bigInteger":
291
+ case "float":
292
+ case "decimal":
293
+ return "number";
294
+ default:
295
+ return "string";
36
296
  }
37
- const typesWithoutExport = processedTypes.replace(/export interface Schema/g, "interface DirectusSchema").replace(/^export interface /gm, "interface ");
38
- return `declare global {
39
- ${typesWithoutExport}
40
297
  }
41
-
42
- export {};
43
- `;
298
+ function escapeStringLiteral(value) {
299
+ switch (typeof value) {
300
+ case "string": {
301
+ const backslashEscaped = value.replace(/\\/g, "\\\\");
302
+ if (/^\w+$/.test(backslashEscaped)) {
303
+ const singleQuoteEscaped = backslashEscaped.replace(/'/g, "\\'");
304
+ return `'${singleQuoteEscaped}'`;
305
+ }
306
+ const templateLiteralEscaped = backslashEscaped.replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
307
+ return `\`${templateLiteralEscaped}\``;
308
+ }
309
+ case "boolean":
310
+ case "number":
311
+ return String(value);
312
+ case "undefined":
313
+ return "null";
314
+ default:
315
+ if (value === void 0 || value === null) {
316
+ return "null";
317
+ }
318
+ return JSON.stringify(value);
319
+ }
320
+ }
321
+ function generateJSDocComment(field) {
322
+ const comments = [];
323
+ if (field.meta?.note && !field.meta.note.startsWith("$t")) {
324
+ comments.push(`@description ${field.meta.note}`);
325
+ }
326
+ if (field.schema?.is_primary_key) {
327
+ comments.push("@primaryKey");
328
+ }
329
+ if (field.meta?.required) {
330
+ comments.push("@required");
331
+ }
332
+ return comments.length > 0 ? ` /** ${comments.join(" ")} */
333
+ ` : "";
334
+ }
335
+ function pascalCase(value) {
336
+ return value.split(/[\s_-]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
337
+ }
338
+ const singularizeExceptions = /* @__PURE__ */ new Map([
339
+ // invariant (same singular/plural)
340
+ ["sheep", "sheep"],
341
+ ["fish", "fish"],
342
+ ["series", "series"],
343
+ ["species", "species"],
344
+ ["deer", "deer"],
345
+ ["aircraft", "aircraft"],
346
+ ["news", "news"],
347
+ // irregular
348
+ ["children", "child"],
349
+ ["men", "man"],
350
+ ["women", "woman"],
351
+ ["teeth", "tooth"],
352
+ ["feet", "foot"],
353
+ ["mice", "mouse"],
354
+ ["geese", "goose"],
355
+ ["oxen", "ox"],
356
+ // -ves special cases (fe endings)
357
+ ["knives", "knife"],
358
+ ["wives", "wife"],
359
+ ["lives", "life"],
360
+ ["leaves", "leaf"],
361
+ ["loaves", "loaf"],
362
+ ["wolves", "wolf"],
363
+ ["calves", "calf"],
364
+ ["halves", "half"],
365
+ ["selves", "self"],
366
+ ["elves", "elf"],
367
+ // Latin / Greek
368
+ ["analyses", "analysis"],
369
+ ["diagnoses", "diagnosis"],
370
+ ["theses", "thesis"],
371
+ ["crises", "crisis"],
372
+ ["phenomena", "phenomenon"],
373
+ ["criteria", "criterion"],
374
+ // -us -> -i
375
+ ["cacti", "cactus"],
376
+ ["fungi", "fungus"],
377
+ ["nuclei", "nucleus"],
378
+ ["syllabi", "syllabus"],
379
+ // -a -> -um
380
+ ["bacteria", "bacterium"],
381
+ ["curricula", "curriculum"],
382
+ // exceptions to oes -> o
383
+ ["toes", "toe"]
384
+ ]);
385
+ const singularizeRules = [
386
+ // ies -> y
387
+ (word) => {
388
+ if (word.endsWith("ies") && word.length > 3) {
389
+ return `${word.slice(0, -3)}y`;
390
+ }
391
+ return null;
392
+ },
393
+ // oes -> o
394
+ (word) => {
395
+ if (word.endsWith("oes")) {
396
+ return word.slice(0, -2);
397
+ }
398
+ return null;
399
+ },
400
+ // es -> remove (ch, sh, s, x, z)
401
+ (word) => {
402
+ if (/(?:ch|sh|[sxz])es$/.test(word)) {
403
+ return word.slice(0, -2);
404
+ }
405
+ return null;
406
+ },
407
+ // fallback: trailing s (guarded)
408
+ (word) => {
409
+ if (word.endsWith("s") && !word.endsWith("ss") && word.length > 3 && !word.endsWith("us") && !word.endsWith("is")) {
410
+ return word.slice(0, -1);
411
+ }
412
+ return null;
413
+ }
414
+ ];
415
+ function singularize(word) {
416
+ if (!word || typeof word !== "string")
417
+ return "";
418
+ const lower = word.toLowerCase();
419
+ const exception = singularizeExceptions.get(lower);
420
+ if (exception)
421
+ return exception;
422
+ for (const rule of singularizeRules) {
423
+ const result = rule(lower);
424
+ if (result)
425
+ return result;
426
+ }
427
+ return word;
44
428
  }
@@ -1 +1 @@
1
- export { generateTypes } from './generate.js';
1
+ export { FALLBACK_TYPE_STRING, generateTypesFromDirectus } from './generate.js';
@@ -1 +1 @@
1
- export { generateTypes } from "./generate.js";
1
+ export { FALLBACK_TYPE_STRING, generateTypesFromDirectus } from "./generate.js";
@@ -1413,4 +1413,4 @@ function formatPushResult(result) {
1413
1413
  return lines.join("\n");
1414
1414
  }
1415
1415
 
1416
- export { loadRulesFromJsonFile as a, loadRulesFromPayload as b, loadRulesFromPayloadFile as c, compareRulesPayloads as d, diffRemoteRules as e, diffRules as f, fetchRemoteRules as g, fetchRemoteRulesAsJson as h, isStandardSchema as i, formatDiff as j, formatPushResult as k, loadRulesFromJson as l, pushRules as m, normalizeRules as n, serializeToJson as o, pullRules as p, isValidationStandardSchema as q, rulesToJson as r, serializeToDirectusApi as s, toDirectusValidation as t };
1416
+ export { diffRules as a, fetchRemoteRulesAsJson as b, compareRulesPayloads as c, diffRemoteRules as d, formatDiff as e, fetchRemoteRules as f, formatPushResult as g, isValidationStandardSchema as h, isStandardSchema as i, loadRulesFromJsonFile as j, loadRulesFromPayload as k, loadRulesFromJson as l, loadRulesFromPayloadFile as m, normalizeRules as n, pushRules as o, pullRules as p, serializeToJson as q, rulesToJson as r, serializeToDirectusApi as s, toDirectusValidation as t };
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "nuxt-directus-sdk",
3
3
  "type": "module",
4
- "version": "5.0.1",
4
+ "version": "6.0.0-beta.0",
5
+ "packageManager": "pnpm@10.32.1",
5
6
  "description": "A Directus nuxt module that uses the Directus SDK",
6
7
  "author": "Matthew Rollinson <matt@rolley.io>",
7
8
  "license": "MIT",
@@ -30,22 +31,23 @@
30
31
  ],
31
32
  "scripts": {
32
33
  "prepack": "nuxt-module-build build",
33
- "dev": "nuxi dev playground",
34
- "dev:build": "nuxi build playground",
35
- "dev:prepare": "nuxt-module-build --stub && nuxi prepare playground",
36
- "release": "npm run lint && npm run prepack && npm publish && git push --follow-tags",
37
- "release:next": "npm run lint && npm run prepack && npm publish --tag next && git push --follow-tags",
38
- "lint": "eslint . --fix",
34
+ "dev": "pnpm run dev:prepare && nuxt dev playground",
35
+ "dev:build": "nuxt build playground",
36
+ "dev:prepare": "nuxt-module-build build --stub && nuxt prepare playground",
37
+ "release": "pnpm run lint && pnpm run test && pnpm run prepack && changelogen --release --push --no-github",
38
+ "release:next": "pnpm run lint && pnpm run test && pnpm run prepack && changelogen --release --prerelease beta --push --no-github",
39
+ "lint": "eslint .",
40
+ "lint:fix": "eslint . --fix",
39
41
  "test": "vitest run",
40
42
  "test:watch": "vitest watch",
41
- "docs:dev": "bun run dev:prepare && vitepress dev docs",
42
- "docs:build": "bun run dev:prepare && vitepress build docs",
43
+ "docs:dev": "pnpm run dev:prepare && vitepress dev docs",
44
+ "docs:build": "pnpm run dev:prepare && vitepress build docs",
43
45
  "docs:preview": "vitepress preview docs"
44
46
  },
45
47
  "peerDependencies": {
46
- "@directus/sdk": ">=20.0.0",
47
- "@directus/types": ">=13.0.0",
48
- "@directus/visual-editing": ">=1.1.0",
48
+ "@directus/sdk": ">=21.0.0",
49
+ "@directus/types": ">=15.0.0",
50
+ "@directus/visual-editing": ">=2.0.0",
49
51
  "@nuxt/image": ">=2.0.0"
50
52
  },
51
53
  "peerDependenciesMeta": {
@@ -60,29 +62,28 @@
60
62
  }
61
63
  },
62
64
  "dependencies": {
63
- "@nuxt/kit": "^4.0.0",
64
- "change-case": "^4.1.2",
65
- "defu": "^6.1.1",
66
- "directus-sdk-typegen": "^0.2.1",
65
+ "@nuxt/kit": "^4.4.2",
66
+ "defu": "^6.1.7",
67
67
  "http-proxy": "^1.18.1",
68
- "ufo": "^1.6.1"
68
+ "ufo": "^1.6.3"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@antfu/eslint-config": "^4.10.0",
72
- "@directus/sdk": "^20.0.0",
73
- "@directus/types": "^13.0.0",
74
- "@directus/visual-editing": "^1.1.0",
75
- "@nuxt/devtools": "latest",
72
+ "@directus/sdk": "^21.2.2",
73
+ "@directus/types": "^15.0.2",
74
+ "@directus/visual-editing": "^2.0.0",
75
+ "@nuxt/devtools": "^3.2.4",
76
76
  "@nuxt/image": "^2.0.0",
77
77
  "@nuxt/module-builder": "^1.0.2",
78
- "@nuxt/schema": "^4.1.2",
79
- "@nuxt/test-utils": "^3.19.2",
78
+ "@nuxt/schema": "^4.4.2",
79
+ "@nuxt/test-utils": "^4.0.2",
80
80
  "@types/http-proxy": "^1.17.17",
81
81
  "changelogen": "^0.6.1",
82
82
  "eslint": "^9.22.0",
83
- "nuxt": "^4.1.2",
84
- "vitepress": "^1.6.3",
85
- "vitest": "^3.0.8",
83
+ "nuxt": "^4.4.2",
84
+ "typescript": "^6.0.3",
85
+ "vitepress": "^1.6.4",
86
+ "vitest": "^4.1.4",
86
87
  "vue-tsc": "^3.0.8"
87
88
  },
88
89
  "unbuild": {