nuxt-directus-sdk 0.0.2

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.
Files changed (34) hide show
  1. package/README.md +84 -0
  2. package/dist/module.cjs +5 -0
  3. package/dist/module.d.mts +77 -0
  4. package/dist/module.d.ts +77 -0
  5. package/dist/module.json +9 -0
  6. package/dist/module.mjs +224 -0
  7. package/dist/runtime/composables/auth.d.ts +22 -0
  8. package/dist/runtime/composables/auth.mjs +83 -0
  9. package/dist/runtime/composables/directus.d.ts +2 -0
  10. package/dist/runtime/composables/directus.mjs +35 -0
  11. package/dist/runtime/composables/domain.d.ts +4 -0
  12. package/dist/runtime/composables/domain.mjs +11 -0
  13. package/dist/runtime/composables/files.d.ts +6 -0
  14. package/dist/runtime/composables/files.mjs +38 -0
  15. package/dist/runtime/composables/toast.d.ts +15 -0
  16. package/dist/runtime/composables/toast.mjs +41 -0
  17. package/dist/runtime/composables/tokens.d.ts +9 -0
  18. package/dist/runtime/composables/tokens.mjs +53 -0
  19. package/dist/runtime/oas/index.d.ts +5 -0
  20. package/dist/runtime/oas/index.mjs +100 -0
  21. package/dist/runtime/oas/types.d.ts +27 -0
  22. package/dist/runtime/oas/types.mjs +0 -0
  23. package/dist/runtime/plugin.client.d.ts +2 -0
  24. package/dist/runtime/plugin.client.mjs +8 -0
  25. package/dist/runtime/plugin.d.ts +2 -0
  26. package/dist/runtime/plugin.mjs +35 -0
  27. package/dist/runtime/server/services/directus.d.ts +5 -0
  28. package/dist/runtime/server/services/directus.mjs +28 -0
  29. package/dist/runtime/server/services/index.d.ts +1 -0
  30. package/dist/runtime/server/services/index.mjs +1 -0
  31. package/dist/runtime/types/index.d.ts +38 -0
  32. package/dist/types.d.mts +15 -0
  33. package/dist/types.d.ts +15 -0
  34. package/package.json +54 -0
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # directus-nuxt-sdk
2
+
3
+ [![npm version][npm-version-src]][npm-version-href]
4
+ [![npm downloads][npm-downloads-src]][npm-downloads-href]
5
+ [![License][license-src]][license-href]
6
+ [![Nuxt][nuxt-src]][nuxt-href]
7
+
8
+ > A bunch of directus helpers for nuxt applications
9
+
10
+ - [✨  Release Notes](/CHANGELOG.md)
11
+
12
+ ## Features
13
+
14
+ - ⛰  Built-in Directus auth and type generation
15
+ - 🚠  Built-in helpers and utils
16
+
17
+ ## Quick Setup
18
+
19
+ 1. Add `directus-nuxt-sdk` dependency to your project
20
+
21
+ ```bash
22
+ # Using pnpm
23
+ pnpm add -D directus-nuxt-sdk
24
+
25
+ # Using yarn
26
+ yarn add --dev directus-nuxt-sdk
27
+
28
+ # Using npm
29
+ npm install --save-dev directus-nuxt-sdk
30
+
31
+ # Using bun
32
+ bun install --save-dev directus-nuxt-sdk
33
+ ```
34
+
35
+ 2. Add `directus-nuxt-sdk` to the `modules` section of `nuxt.config.ts`
36
+
37
+ ```js
38
+ export default defineNuxtConfig({
39
+ modules: [
40
+ 'directus-nuxt-sdk'
41
+ ]
42
+ })
43
+ ```
44
+
45
+ That's it! You can now use Directus your Nuxt app ✨
46
+
47
+ ## Development
48
+
49
+ ```bash
50
+ # Install dependencies
51
+ npm install
52
+
53
+ # Generate type stubs
54
+ npm run dev:prepare
55
+
56
+ # Develop with the playground
57
+ npm run dev
58
+
59
+ # Build the playground
60
+ npm run dev:build
61
+
62
+ # Run ESLint
63
+ npm run lint
64
+
65
+ # Run Vitest
66
+ npm run test
67
+ npm run test:watch
68
+
69
+ # Release new version
70
+ npm run release
71
+ ```
72
+
73
+ <!-- Badges -->
74
+ [npm-version-src]: https://img.shields.io/npm/v/directus-nuxt-sdk/latest.svg?style=flat&colorA=18181B&colorB=28CF8D
75
+ [npm-version-href]: https://npmjs.com/package/directus-nuxt-sdk
76
+
77
+ [npm-downloads-src]: https://img.shields.io/npm/dm/directus-nuxt-sdk.svg?style=flat&colorA=18181B&colorB=28CF8D
78
+ [npm-downloads-href]: https://npmjs.com/package/directus-nuxt-sdk
79
+
80
+ [license-src]: https://img.shields.io/npm/l/directus-nuxt-sdk.svg?style=flat&colorA=18181B&colorB=28CF8D
81
+ [license-href]: https://npmjs.com/package/directus-nuxt-sdk
82
+
83
+ [nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
84
+ [nuxt-href]: https://nuxt.com
@@ -0,0 +1,5 @@
1
+ module.exports = function(...args) {
2
+ return import('./module.mjs').then(m => m.default.call(this, ...args))
3
+ }
4
+ const _meta = module.exports.meta = require('./module.json')
5
+ module.exports.getMeta = () => Promise.resolve(_meta)
@@ -0,0 +1,77 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+ import { Query } from '@directus/sdk';
3
+ import { DirectusCollections } from '#build/types/directus';
4
+
5
+ interface ModuleOptions {
6
+ /**
7
+ * Directus API URL
8
+ * @default process.env.DIRECTUS_URL
9
+ * @type string
10
+ */
11
+ url?: string;
12
+ /**
13
+ * Admin Auth Token used for generating types and server functions
14
+ * @default process.env.DIRECTUS_ADMIN_TOKEN
15
+ * @type string
16
+ */
17
+ adminToken?: string;
18
+ /**
19
+ * Fetch the user serverside
20
+ *
21
+ * @default true
22
+ */
23
+ fetchUser?: boolean;
24
+ /**
25
+ * Directus Auth Options
26
+ * @default {}
27
+ * @type Query<DirectusCollections, 'directus_users'>
28
+ */
29
+ fetchUserParams?: Query<DirectusCollections, 'directus_users'>;
30
+ /**
31
+ * Add Directus Admin in Nuxt Devtools
32
+ *
33
+ * @default false
34
+ */
35
+ devtools?: boolean;
36
+ /**
37
+ * Token Cookie Name
38
+ * @type string
39
+ * @ default 'directus_token'
40
+ */
41
+ cookieNameToken?: string;
42
+ /**
43
+ * Refresh Token Cookie Name
44
+ * @type string
45
+ * @default 'directus_refresh_token'
46
+ */
47
+ cookieNameRefreshToken?: string;
48
+ /**
49
+ * The max age for auth cookies in seconds.
50
+ * This should match your directus env key AUTH_TOKEN_TTL
51
+ * @type string
52
+ * @default 900
53
+ */
54
+ cookieMaxAge?: number;
55
+ /**
56
+ * The max age for auth cookies in seconds.
57
+ * This should match your directus env key REFRESH_TOKEN_TTL
58
+ * @type string
59
+ * @default 604800
60
+ */
61
+ cookieMaxAgeRefreshToken?: number;
62
+ /**
63
+ * The SameSite attribute for auth cookies.
64
+ * @type string
65
+ * @default 'lax'
66
+ */
67
+ cookieSameSite?: 'strict' | 'lax' | 'none' | undefined;
68
+ /**
69
+ * The Secure attribute for auth cookies.
70
+ * @type boolean
71
+ * @default false
72
+ */
73
+ cookieSecure?: boolean;
74
+ }
75
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
76
+
77
+ export { type ModuleOptions, _default as default };
@@ -0,0 +1,77 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+ import { Query } from '@directus/sdk';
3
+ import { DirectusCollections } from '#build/types/directus';
4
+
5
+ interface ModuleOptions {
6
+ /**
7
+ * Directus API URL
8
+ * @default process.env.DIRECTUS_URL
9
+ * @type string
10
+ */
11
+ url?: string;
12
+ /**
13
+ * Admin Auth Token used for generating types and server functions
14
+ * @default process.env.DIRECTUS_ADMIN_TOKEN
15
+ * @type string
16
+ */
17
+ adminToken?: string;
18
+ /**
19
+ * Fetch the user serverside
20
+ *
21
+ * @default true
22
+ */
23
+ fetchUser?: boolean;
24
+ /**
25
+ * Directus Auth Options
26
+ * @default {}
27
+ * @type Query<DirectusCollections, 'directus_users'>
28
+ */
29
+ fetchUserParams?: Query<DirectusCollections, 'directus_users'>;
30
+ /**
31
+ * Add Directus Admin in Nuxt Devtools
32
+ *
33
+ * @default false
34
+ */
35
+ devtools?: boolean;
36
+ /**
37
+ * Token Cookie Name
38
+ * @type string
39
+ * @ default 'directus_token'
40
+ */
41
+ cookieNameToken?: string;
42
+ /**
43
+ * Refresh Token Cookie Name
44
+ * @type string
45
+ * @default 'directus_refresh_token'
46
+ */
47
+ cookieNameRefreshToken?: string;
48
+ /**
49
+ * The max age for auth cookies in seconds.
50
+ * This should match your directus env key AUTH_TOKEN_TTL
51
+ * @type string
52
+ * @default 900
53
+ */
54
+ cookieMaxAge?: number;
55
+ /**
56
+ * The max age for auth cookies in seconds.
57
+ * This should match your directus env key REFRESH_TOKEN_TTL
58
+ * @type string
59
+ * @default 604800
60
+ */
61
+ cookieMaxAgeRefreshToken?: number;
62
+ /**
63
+ * The SameSite attribute for auth cookies.
64
+ * @type string
65
+ * @default 'lax'
66
+ */
67
+ cookieSameSite?: 'strict' | 'lax' | 'none' | undefined;
68
+ /**
69
+ * The Secure attribute for auth cookies.
70
+ * @type boolean
71
+ * @default false
72
+ */
73
+ cookieSecure?: boolean;
74
+ }
75
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
76
+
77
+ export { type ModuleOptions, _default as default };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "nuxt-directus-sdk",
3
+ "version": "0.0.2",
4
+ "configKey": "rolley",
5
+ "compatibility": {
6
+ "nuxt": "^3.0.0",
7
+ "bridge": false
8
+ }
9
+ }
@@ -0,0 +1,224 @@
1
+ import { defu } from 'defu';
2
+ import { defineNuxtModule, createResolver, addPlugin, addImportsDir, addComponentsDir, addTypeTemplate, logger } from '@nuxt/kit';
3
+ import { joinURL } from 'ufo';
4
+ import { snakeCase } from 'change-case';
5
+ import { createDirectus, authentication, rest, readOpenApiSpec } from '@directus/sdk';
6
+
7
+ const name = "nuxt-directus-sdk";
8
+ const version = "0.0.2";
9
+
10
+ function mapPropertyType(propertyType) {
11
+ return propertyType === "integer" ? "number" : propertyType;
12
+ }
13
+ function processOneOfProperty(schemas, name, property) {
14
+ const ref = property.oneOf.find((item) => {
15
+ return "$ref" in item;
16
+ });
17
+ if (ref) {
18
+ const $ref = ref.$ref;
19
+ const $refKey = $ref.split("/").pop();
20
+ if ($refKey) {
21
+ const $refSchema = `DirectusCollections['${schemas[$refKey]?.["x-collection"]}']`;
22
+ if ($refSchema)
23
+ return $refSchema;
24
+ }
25
+ }
26
+ const firstValue = property.oneOf?.[0];
27
+ if ("type" in firstValue) {
28
+ const firstType = mapPropertyType(firstValue.type);
29
+ if (firstType)
30
+ return firstType;
31
+ }
32
+ console.error("Unknown schema for oneOf", name, property);
33
+ return "";
34
+ }
35
+ function processProperty(schemas, name, property) {
36
+ if ("type" in property) {
37
+ if (property.type === "array" && property.items) {
38
+ if (!property.items.oneOf)
39
+ return processProperty(schemas, name, property.items);
40
+ return `
41
+ "${name}": ${processOneOfProperty(schemas, name, property.items)};
42
+ `;
43
+ } else {
44
+ return `
45
+ ${property.format ? `/* ${property.format} */` : ""}
46
+ "${name}": ${mapPropertyType(property.type ?? "string")};
47
+ `;
48
+ }
49
+ } else if ("oneOf" in property) {
50
+ const value = processOneOfProperty(schemas, name, property);
51
+ if (!value)
52
+ return;
53
+ const simpleTypes = ["string", "number", "boolean"];
54
+ const valueType = simpleTypes.includes(value) ? value : `Single<${value}>`;
55
+ return `
56
+ "${name}": ${valueType};
57
+ `;
58
+ }
59
+ }
60
+ async function generateTypes(options) {
61
+ const directus = createDirectus(options.url).with(authentication("json", { autoRefresh: false })).with(rest());
62
+ directus.setToken(options.token);
63
+ const data = await directus.request(readOpenApiSpec());
64
+ const types = [];
65
+ const schemas = data.components.schemas;
66
+ const schemaNames = [];
67
+ Object.entries(schemas).forEach(([_, schema]) => {
68
+ const schemaName = schema["x-collection"];
69
+ if (!schemaName)
70
+ return;
71
+ if (schema.type !== "object") {
72
+ console.error(schemaName, "is not an object");
73
+ return;
74
+ }
75
+ if (schemaNames.includes(schemaName))
76
+ return;
77
+ schemaNames.push(schemaName);
78
+ const properties = [];
79
+ Object.entries(schema.properties).forEach(([name, property]) => {
80
+ const propertyType = processProperty(schemas, name, property);
81
+ if (propertyType)
82
+ properties.push(propertyType.trim());
83
+ });
84
+ types.push(`${snakeCase(schemaName)}: {
85
+ ${properties.join("\n")}
86
+ }[];
87
+ `);
88
+ });
89
+ const exportProperties = types.join("\n");
90
+ return `
91
+ export type Single<T extends any[]> = T extends (infer U)[] ? U : never;
92
+
93
+ export type DirectusCollections = {
94
+ ${exportProperties}
95
+ };
96
+
97
+ export type DirectusCollectionUser = Single<DirectusCollections['directus_users']>;
98
+
99
+ declare global {
100
+ type Single = Single
101
+ type DirectusCollections = DirectusCollections
102
+ type DirectusCollectionUser = DirectusCollectionUser
103
+ }
104
+
105
+ export {};
106
+ `;
107
+ }
108
+
109
+ const configKey = "rolley";
110
+ const module = defineNuxtModule({
111
+ meta: {
112
+ name,
113
+ version,
114
+ configKey,
115
+ compatibility: {
116
+ nuxt: "^3.0.0",
117
+ bridge: false
118
+ }
119
+ },
120
+ defaults: {
121
+ url: process.env.DIRECTUS_URL,
122
+ adminToken: process.env.DIRECTUS_ADMIN_TOKEN,
123
+ fetchUser: true,
124
+ devtools: false,
125
+ cookieNameToken: "directus_token",
126
+ cookieNameRefreshToken: "directus_refresh_token",
127
+ // Nuxt Cookies Docs @ https://nuxt.com/docs/api/composables/use-cookie
128
+ cookieMaxAge: 900,
129
+ cookieMaxAgeRefreshToken: 604800,
130
+ cookieSameSite: "lax",
131
+ cookieSecure: false
132
+ },
133
+ async setup(options, nuxt) {
134
+ nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
135
+ nuxt.options.runtimeConfig.public[configKey] = defu(nuxt.options.runtimeConfig.public[configKey], {
136
+ url: options.url,
137
+ fetchUser: options.fetchUser,
138
+ fetchUserParams: options.fetchUserParams,
139
+ devtools: options.devtools,
140
+ cookieNameToken: options.cookieNameToken,
141
+ cookieNameRefreshToken: options.cookieNameRefreshToken,
142
+ cookieMaxAge: options.cookieMaxAge,
143
+ cookieMaxAgeRefreshToken: options.cookieMaxAgeRefreshToken,
144
+ cookieSameSite: options.cookieSameSite,
145
+ cookieSecure: options.cookieSecure
146
+ });
147
+ const resolver = createResolver(import.meta.url);
148
+ addPlugin(resolver.resolve("./runtime/plugin"));
149
+ addPlugin({
150
+ src: resolver.resolve("./runtime/plugin.client"),
151
+ mode: "client"
152
+ });
153
+ addImportsDir(resolver.resolve("./runtime/composables"));
154
+ addComponentsDir({
155
+ path: resolver.resolve("./runtime/components"),
156
+ pathPrefix: false,
157
+ prefix: "",
158
+ global: true
159
+ });
160
+ nuxt.options.css.push("vue-toastification/dist/index.css");
161
+ nuxt.options.build.transpile.push("vue-toastification");
162
+ nuxt.hook("nitro:config", (nitroConfig) => {
163
+ nitroConfig.alias = nitroConfig.alias || {};
164
+ nitroConfig.externals = defu(typeof nitroConfig.externals === "object" ? nitroConfig.externals : {}, {
165
+ inline: [resolver.resolve("./runtime")]
166
+ });
167
+ nitroConfig.alias[`#${configKey}`] = resolver.resolve("./runtime/server/services");
168
+ });
169
+ addTypeTemplate({
170
+ filename: `types/${configKey}.d.ts`,
171
+ getContents: () => [
172
+ `declare module '#${configKey}' {`,
173
+ ` const useDirectus: typeof import('${resolver.resolve("./runtime/server/services")}').useDirectus`,
174
+ ` const useAdminDirectus: typeof import('${resolver.resolve("./runtime/server/services")}').useAdminDirectus`,
175
+ ` const useDirectusUrl: typeof import('${resolver.resolve("./runtime/server/services")}').useDirectusUrl`,
176
+ ` const useDirectusAccessToken: typeof import('${resolver.resolve("./runtime/server/services")}').useDirectusAccessToken`,
177
+ "}"
178
+ ].join("\n")
179
+ });
180
+ nuxt.hook("prepare:types", (options2) => {
181
+ options2.references.push({ path: resolver.resolve(nuxt.options.buildDir, `types/${configKey}.d.ts`) });
182
+ });
183
+ if (options.url) {
184
+ const adminUrl = joinURL(options.url, "/admin/");
185
+ logger.info(`Directus Admin URL: ${adminUrl}`);
186
+ if (options.devtools) {
187
+ nuxt.hook("devtools:customTabs", (iframeTabs) => {
188
+ iframeTabs.push({
189
+ name: "directus",
190
+ title: "Directus",
191
+ icon: "simple-icons:directus",
192
+ view: {
193
+ type: "iframe",
194
+ src: adminUrl
195
+ }
196
+ });
197
+ });
198
+ }
199
+ if (options.adminToken) {
200
+ logger.info("Generating Directus types");
201
+ try {
202
+ const typesPath = addTypeTemplate({
203
+ filename: "types/directus.d.ts",
204
+ getContents() {
205
+ return generateTypes({
206
+ url: options.url ?? "",
207
+ token: options.adminToken ?? ""
208
+ });
209
+ }
210
+ }).dst;
211
+ nuxt.hook("prepare:types", (options2) => {
212
+ options2.references.push({ path: typesPath });
213
+ });
214
+ } catch (error) {
215
+ logger.error(error.message);
216
+ }
217
+ } else {
218
+ logger.info("Add DIRECTUS_ADMIN_TOKEN to the .env file to generate directus types");
219
+ }
220
+ }
221
+ }
222
+ });
223
+
224
+ export { module as default };
@@ -0,0 +1,22 @@
1
+ import type { ComputedRef, Ref } from '#imports';
2
+ import type { DirectusCollectionUser } from '#build/types/directus';
3
+ export declare function useDirectusUser(): Ref<any>;
4
+ export interface DirectusAuth {
5
+ user: Ref<DirectusCollectionUser | null>;
6
+ loggedIn: ComputedRef<boolean>;
7
+ refreshTokens(): Promise<void>;
8
+ fetchUser(): Promise<DirectusCollectionUser | null>;
9
+ updateUser(data: Partial<DirectusCollectionUser>): Promise<DirectusCollectionUser | null>;
10
+ login(email: string, password: string): Promise<{
11
+ user: DirectusCollectionUser | null;
12
+ access_token: string;
13
+ refreshToken: string | null;
14
+ expires: number;
15
+ redirect(defaultPath?: string): void;
16
+ }>;
17
+ logout(): Promise<void>;
18
+ register(data: Partial<DirectusCollectionUser>): Promise<DirectusCollectionUser>;
19
+ requestPasswordReset(email: string, resetUrl?: string | null | undefined): Promise<void>;
20
+ resetPassword(token: string, password: string): Promise<void>;
21
+ }
22
+ export declare function useDirectusAuth(): DirectusAuth;
@@ -0,0 +1,83 @@
1
+ import { createUser, passwordRequest, passwordReset, readMe, updateMe } from "@directus/sdk";
2
+ import { useDirectus } from "./directus.mjs";
3
+ import { useDirectusTokens } from "./tokens.mjs";
4
+ import { computed, useState } from "#imports";
5
+ import { useRouter, useRuntimeConfig } from "#app";
6
+ export function useDirectusUser() {
7
+ return useState("directus.user", () => null);
8
+ }
9
+ export function useDirectusAuth() {
10
+ const config = useRuntimeConfig();
11
+ const directus = useDirectus();
12
+ const tokens = useDirectusTokens();
13
+ const user = useDirectusUser();
14
+ const loggedIn = computed(() => user.value !== null);
15
+ async function fetchUser() {
16
+ try {
17
+ if (!tokens.refreshToken.value)
18
+ throw new Error("No refresh token");
19
+ await directus.refresh();
20
+ user.value = await directus.request(readMe(config.public.rolley.fetchUserParams));
21
+ } catch (e) {
22
+ user.value = null;
23
+ }
24
+ return user.value;
25
+ }
26
+ async function updateUser(data) {
27
+ const currentUser = user.value;
28
+ if (!currentUser?.id)
29
+ throw new Error("No user available");
30
+ user.value = await directus.request(updateMe(data, config.public.rolley.fetchUserParams));
31
+ return user.value;
32
+ }
33
+ async function login(email, password, options = {}) {
34
+ const response = await directus.login(email, password, options);
35
+ if (!response.access_token)
36
+ throw new Error("Login failed, please check your credentials.");
37
+ await fetchUser();
38
+ return {
39
+ user: user.value,
40
+ accessToken: response.access_token,
41
+ refreshToken: response.refresh_token,
42
+ expires: response.expires,
43
+ expiresAt: response.expires_at,
44
+ // Allow redirecting to a specific page after login
45
+ redirect(defaultPath = "/") {
46
+ const router = useRouter();
47
+ const route = router.currentRoute.value;
48
+ router.replace({ path: route.query.redirect ? decodeURIComponent(route.query.redirect) : defaultPath });
49
+ }
50
+ };
51
+ }
52
+ async function register(data) {
53
+ return directus.request(createUser(data));
54
+ }
55
+ async function requestPasswordReset(email, resetUrl) {
56
+ directus.request(passwordRequest(email, resetUrl));
57
+ }
58
+ async function resetPassword(token, password) {
59
+ directus.request(passwordReset(token, password));
60
+ }
61
+ async function logout() {
62
+ try {
63
+ await directus.logout();
64
+ } finally {
65
+ user.value = null;
66
+ tokens.refreshToken.value = null;
67
+ tokens.accessToken.value = null;
68
+ tokens.expires.value = null;
69
+ tokens.expiresAt.value = null;
70
+ }
71
+ }
72
+ return {
73
+ user,
74
+ loggedIn,
75
+ fetchUser,
76
+ updateUser,
77
+ register,
78
+ login,
79
+ logout,
80
+ requestPasswordReset,
81
+ resetPassword
82
+ };
83
+ }
@@ -0,0 +1,2 @@
1
+ export declare function useDirectusUrl(): string;
2
+ export declare function useDirectus(token?: string): import("@directus/sdk/dist/client-e8d6bf91").D<DirectusCollections> & import("@directus/sdk/dist/login-0506af09").d<DirectusCollections> & import("@directus/sdk/dist/login-0506af09").f<DirectusCollections> & import("@directus/sdk/dist/output-35b496cf").d<DirectusCollections>;
@@ -0,0 +1,35 @@
1
+ import { authentication, createDirectus, realtime, rest } from "@directus/sdk";
2
+ import { useDirectusTokens } from "./tokens.mjs";
3
+ import { useRuntimeConfig } from "#app";
4
+ export function useDirectusUrl() {
5
+ return useRuntimeConfig().public.rolley.url;
6
+ }
7
+ function createDirectusStorage() {
8
+ const tokens = useDirectusTokens();
9
+ return {
10
+ get() {
11
+ return {
12
+ access_token: tokens.accessToken.value,
13
+ refresh_token: tokens.refreshToken.value,
14
+ expires: tokens.expires.value,
15
+ expires_at: tokens.expiresAt.value
16
+ };
17
+ },
18
+ set(value) {
19
+ tokens.accessToken.value = value?.access_token ?? null;
20
+ tokens.refreshToken.value = value?.refresh_token ?? null;
21
+ tokens.expires.value = value?.expires ?? null;
22
+ tokens.expiresAt.value = value?.expires_at ?? null;
23
+ }
24
+ };
25
+ }
26
+ export function useDirectus(token) {
27
+ const url = useDirectusUrl();
28
+ const directus = createDirectus(url).with(authentication("json", {
29
+ storage: createDirectusStorage(),
30
+ autoRefresh: token !== ""
31
+ })).with(rest()).with(realtime());
32
+ if (token)
33
+ directus.setToken(token);
34
+ return directus;
35
+ }
@@ -0,0 +1,4 @@
1
+ import type { Ref } from '#imports';
2
+ export declare function useDomain(): Ref<string>;
3
+ export declare function useSubdomain(): Ref<string>;
4
+ export declare function getSubdomainLink(slug: string): string;
@@ -0,0 +1,11 @@
1
+ import { useState } from "#imports";
2
+ export function useDomain() {
3
+ return useState("domain", () => "");
4
+ }
5
+ export function useSubdomain() {
6
+ return useState("subdomain", () => "");
7
+ }
8
+ export function getSubdomainLink(slug) {
9
+ const domain = useDomain();
10
+ return `//${slug}.${domain.value}/`;
11
+ }
@@ -0,0 +1,6 @@
1
+ import type { DirectusThumbnailOptions } from '../types';
2
+ export declare function uploadDirectusFile(file: File, folder?: string): Promise<Record<string, any>>;
3
+ export declare function getDirectusAssetUrl(fileId: string, options?: {
4
+ token?: string;
5
+ }): string;
6
+ export declare function getDirectusThumbnailUrl(fileId: string, options?: DirectusThumbnailOptions): string;
@@ -0,0 +1,38 @@
1
+ import { uploadFiles } from "@directus/sdk";
2
+ import { useDirectus, useDirectusUrl } from "./directus.mjs";
3
+ export async function uploadDirectusFile(file, folder) {
4
+ const directus = useDirectus();
5
+ const formData = new FormData();
6
+ formData.set("file", file);
7
+ if (folder)
8
+ formData.set("folder", folder);
9
+ return directus.request(uploadFiles(formData));
10
+ }
11
+ export function getDirectusAssetUrl(fileId, options) {
12
+ const directusUrl = useDirectusUrl();
13
+ const url = new URL(`${directusUrl}assets/${fileId}`);
14
+ if (options?.token)
15
+ url.searchParams.append("access_token", options.token);
16
+ return url.href;
17
+ }
18
+ export function getDirectusThumbnailUrl(fileId, options) {
19
+ const directusUrl = useDirectusUrl();
20
+ const url = new URL(`${directusUrl}assets/${fileId}`);
21
+ if (options) {
22
+ if (options.width)
23
+ url.searchParams.append("width", options.width.toFixed(0));
24
+ if (options.height)
25
+ url.searchParams.append("height", options.height.toFixed(0));
26
+ if (options.quality)
27
+ url.searchParams.append("quality", options.quality.toFixed(0));
28
+ if (options.withoutEnlargement)
29
+ url.searchParams.append("withoutEnlargement", "true");
30
+ if (options.fit)
31
+ url.searchParams.append("fit", options.fit);
32
+ if (options.format)
33
+ url.searchParams.append("format", options.format);
34
+ if (options.token)
35
+ url.searchParams.append("access_token", options.token);
36
+ }
37
+ return url.href;
38
+ }
@@ -0,0 +1,15 @@
1
+ export declare function useToast(): {
2
+ log: (message: string) => Promise<void>;
3
+ success: (message: string, options?: (import("vue-toastification/dist/types/types").ToastOptions & {
4
+ type?: import("vue-toastification").TYPE.SUCCESS | undefined;
5
+ }) | undefined) => Promise<void>;
6
+ info: (message: string, options?: (import("vue-toastification/dist/types/types").ToastOptions & {
7
+ type?: import("vue-toastification").TYPE.INFO | undefined;
8
+ }) | undefined) => Promise<void>;
9
+ warn: (message: string, options?: (import("vue-toastification/dist/types/types").ToastOptions & {
10
+ type?: import("vue-toastification").TYPE.WARNING | undefined;
11
+ }) | undefined) => Promise<void>;
12
+ error: (message?: string, options?: (import("vue-toastification/dist/types/types").ToastOptions & {
13
+ type?: import("vue-toastification").TYPE.ERROR | undefined;
14
+ }) | undefined) => Promise<void>;
15
+ };
@@ -0,0 +1,41 @@
1
+ import { useToast as useToastFn } from "vue-toastification";
2
+ export function useToast() {
3
+ const toast = useToastFn();
4
+ async function log(message) {
5
+ if (process.client)
6
+ toast(message);
7
+ else
8
+ console.log(message);
9
+ }
10
+ async function success(message, options) {
11
+ if (process.client)
12
+ toast.success(message, options);
13
+ else
14
+ console.log("success", message);
15
+ }
16
+ async function info(message, options) {
17
+ if (process.client)
18
+ toast.info(message, options);
19
+ else
20
+ console.info(message);
21
+ }
22
+ async function warn(message, options) {
23
+ if (process.client)
24
+ toast.warning(message, options);
25
+ else
26
+ console.warn(message);
27
+ }
28
+ async function error(message = "Something has gone wrong, please try again later.", options) {
29
+ if (process.client)
30
+ toast.error(message, options);
31
+ else
32
+ console.error(message);
33
+ }
34
+ return {
35
+ log,
36
+ success,
37
+ info,
38
+ warn,
39
+ error
40
+ };
41
+ }
@@ -0,0 +1,9 @@
1
+ import type { CookieRef } from '#app';
2
+ export declare function getCookieDomain(): string | undefined;
3
+ export interface DirectusTokens {
4
+ accessToken: CookieRef<string | null>;
5
+ refreshToken: CookieRef<string | null>;
6
+ expires: CookieRef<number | null>;
7
+ expiresAt: CookieRef<number | null>;
8
+ }
9
+ export declare function useDirectusTokens(): DirectusTokens;
@@ -0,0 +1,53 @@
1
+ import { useDomain } from "./domain.mjs";
2
+ import { useCookie, useNuxtApp, useRuntimeConfig } from "#app";
3
+ export function getCookieDomain() {
4
+ const domain = useDomain();
5
+ return !domain.value.includes("localhost") ? `.${domain.value}` : void 0;
6
+ }
7
+ function directusCookie(name, cookieOptions) {
8
+ const nuxtApp = useNuxtApp();
9
+ nuxtApp._cookies = nuxtApp._cookies || {};
10
+ if (nuxtApp._cookies[name])
11
+ return nuxtApp._cookies[name];
12
+ const cookie = useCookie(name, cookieOptions);
13
+ nuxtApp._cookies[name] = cookie;
14
+ return cookie;
15
+ }
16
+ export function useDirectusTokens() {
17
+ const config = useRuntimeConfig().public.rolley;
18
+ const sharedOptions = {
19
+ sameSite: config.cookieSameSite,
20
+ secure: config.cookieSecure,
21
+ domain: getCookieDomain()
22
+ };
23
+ function accessToken() {
24
+ return directusCookie(config.cookieNameToken, {
25
+ ...sharedOptions,
26
+ maxAge: config.cookieMaxAge
27
+ });
28
+ }
29
+ function refreshToken() {
30
+ return directusCookie(config.cookieNameRefreshToken, {
31
+ ...sharedOptions,
32
+ maxAge: config.cookieMaxAgeRefreshToken
33
+ });
34
+ }
35
+ function expires() {
36
+ return directusCookie("directus_access_expires", {
37
+ ...sharedOptions,
38
+ maxAge: config.cookieMaxAge
39
+ });
40
+ }
41
+ function expiresAt() {
42
+ return directusCookie("directus_access_expires_at", {
43
+ ...sharedOptions,
44
+ maxAge: config.cookieMaxAge
45
+ });
46
+ }
47
+ return {
48
+ accessToken: accessToken(),
49
+ refreshToken: refreshToken(),
50
+ expires: expires(),
51
+ expiresAt: expiresAt()
52
+ };
53
+ }
@@ -0,0 +1,5 @@
1
+ import type { GenerateTypeOptions, SchemaProperty, SchemaPropertyOneOf, SchemaPropertyType, SchemasRecord } from './types';
2
+ export declare function mapPropertyType(propertyType: SchemaPropertyType): "string" | "boolean" | "array" | "number";
3
+ export declare function processOneOfProperty(schemas: SchemasRecord, name: string, property: SchemaPropertyOneOf): string | undefined;
4
+ export declare function processProperty(schemas: SchemasRecord, name: string, property: SchemaProperty): string | undefined;
5
+ export declare function generateTypes(options: GenerateTypeOptions): Promise<string>;
@@ -0,0 +1,100 @@
1
+ import { snakeCase } from "change-case";
2
+ import { authentication, createDirectus, readOpenApiSpec, rest } from "@directus/sdk";
3
+ export function mapPropertyType(propertyType) {
4
+ return propertyType === "integer" ? "number" : propertyType;
5
+ }
6
+ export function processOneOfProperty(schemas, name, property) {
7
+ const ref = property.oneOf.find((item) => {
8
+ return "$ref" in item;
9
+ });
10
+ if (ref) {
11
+ const $ref = ref.$ref;
12
+ const $refKey = $ref.split("/").pop();
13
+ if ($refKey) {
14
+ const $refSchema = `DirectusCollections['${schemas[$refKey]?.["x-collection"]}']`;
15
+ if ($refSchema)
16
+ return $refSchema;
17
+ }
18
+ }
19
+ const firstValue = property.oneOf?.[0];
20
+ if ("type" in firstValue) {
21
+ const firstType = mapPropertyType(firstValue.type);
22
+ if (firstType)
23
+ return firstType;
24
+ }
25
+ console.error("Unknown schema for oneOf", name, property);
26
+ return "";
27
+ }
28
+ export function processProperty(schemas, name, property) {
29
+ if ("type" in property) {
30
+ if (property.type === "array" && property.items) {
31
+ if (!property.items.oneOf)
32
+ return processProperty(schemas, name, property.items);
33
+ return `
34
+ "${name}": ${processOneOfProperty(schemas, name, property.items)};
35
+ `;
36
+ } else {
37
+ return `
38
+ ${property.format ? `/* ${property.format} */` : ""}
39
+ "${name}": ${mapPropertyType(property.type ?? "string")};
40
+ `;
41
+ }
42
+ } else if ("oneOf" in property) {
43
+ const value = processOneOfProperty(schemas, name, property);
44
+ if (!value)
45
+ return;
46
+ const simpleTypes = ["string", "number", "boolean"];
47
+ const valueType = simpleTypes.includes(value) ? value : `Single<${value}>`;
48
+ return `
49
+ "${name}": ${valueType};
50
+ `;
51
+ }
52
+ }
53
+ export async function generateTypes(options) {
54
+ const directus = createDirectus(options.url).with(authentication("json", { autoRefresh: false })).with(rest());
55
+ directus.setToken(options.token);
56
+ const data = await directus.request(readOpenApiSpec());
57
+ const types = [];
58
+ const schemas = data.components.schemas;
59
+ const schemaNames = [];
60
+ Object.entries(schemas).forEach(([_, schema]) => {
61
+ const schemaName = schema["x-collection"];
62
+ if (!schemaName)
63
+ return;
64
+ if (schema.type !== "object") {
65
+ console.error(schemaName, "is not an object");
66
+ return;
67
+ }
68
+ if (schemaNames.includes(schemaName))
69
+ return;
70
+ schemaNames.push(schemaName);
71
+ const properties = [];
72
+ Object.entries(schema.properties).forEach(([name, property]) => {
73
+ const propertyType = processProperty(schemas, name, property);
74
+ if (propertyType)
75
+ properties.push(propertyType.trim());
76
+ });
77
+ types.push(`${snakeCase(schemaName)}: {
78
+ ${properties.join("\n")}
79
+ }[];
80
+ `);
81
+ });
82
+ const exportProperties = types.join("\n");
83
+ return `
84
+ export type Single<T extends any[]> = T extends (infer U)[] ? U : never;
85
+
86
+ export type DirectusCollections = {
87
+ ${exportProperties}
88
+ };
89
+
90
+ export type DirectusCollectionUser = Single<DirectusCollections['directus_users']>;
91
+
92
+ declare global {
93
+ type Single = Single
94
+ type DirectusCollections = DirectusCollections
95
+ type DirectusCollectionUser = DirectusCollectionUser
96
+ }
97
+
98
+ export {};
99
+ `;
100
+ }
@@ -0,0 +1,27 @@
1
+ export interface GenerateTypeOptions {
2
+ url: string;
3
+ token: string;
4
+ collections?: string[];
5
+ }
6
+ export type SchemaPropertyType = 'string' | 'integer' | 'boolean' | 'array';
7
+ export interface SchemaPropertyOneOfReference {
8
+ $ref: string;
9
+ }
10
+ export type SchemaPropertyOneOfValue = SchemaPropertyOneOfReference | SchemaPropertySingle[];
11
+ export interface SchemaPropertyOneOf {
12
+ nullable: boolean;
13
+ oneOf: SchemaPropertyOneOfValue[];
14
+ }
15
+ export interface SchemaPropertySingle {
16
+ nullable: boolean;
17
+ type?: SchemaPropertyType;
18
+ format?: string;
19
+ items?: SchemaPropertyOneOf;
20
+ }
21
+ export type SchemaProperty = SchemaPropertySingle | SchemaPropertyOneOf;
22
+ export interface Schema {
23
+ type: string;
24
+ 'x-collection'?: string;
25
+ properties: Record<string, SchemaProperty>;
26
+ }
27
+ export type SchemasRecord = Record<string, Schema>;
File without changes
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,8 @@
1
+ import Toast, { POSITION } from "vue-toastification";
2
+ import { defineNuxtPlugin } from "#app";
3
+ export default defineNuxtPlugin(async (nuxt) => {
4
+ const options = {
5
+ position: POSITION.BOTTOM_RIGHT
6
+ };
7
+ nuxt.vueApp.use(Toast, options);
8
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,35 @@
1
+ import { fromUrl, parseDomain } from "parse-domain";
2
+ import { useDomain, useSubdomain } from "./composables/domain.mjs";
3
+ import { useDirectusAuth } from "./composables/auth.mjs";
4
+ import { addRouteMiddleware, defineNuxtPlugin, navigateTo, useRuntimeConfig } from "#app";
5
+ export default defineNuxtPlugin(async (nuxt) => {
6
+ const domain = useDomain();
7
+ const subdomain = useSubdomain();
8
+ if (nuxt.ssrContext) {
9
+ const { host } = nuxt.ssrContext.event.node.req.headers;
10
+ if (host) {
11
+ const { labels, domain: mainDomain, topLevelDomains } = parseDomain(
12
+ fromUrl(host)
13
+ );
14
+ if (mainDomain)
15
+ domain.value = `${mainDomain}.${topLevelDomains.join(".")}`;
16
+ else if (host.includes("localhost"))
17
+ domain.value = host.split(".").at(-1) ?? "";
18
+ else
19
+ domain.value = host;
20
+ subdomain.value = labels[0];
21
+ }
22
+ }
23
+ const config = useRuntimeConfig();
24
+ if (config.public.rolley.fetchUser)
25
+ await useDirectusAuth().fetchUser();
26
+ addRouteMiddleware("auth", async (to) => {
27
+ const user = useDirectusAuth().user;
28
+ if (!user.value) {
29
+ return navigateTo({
30
+ path: "/login",
31
+ query: { redirect: to.path !== "/" ? encodeURIComponent(to.path) : void 0 }
32
+ });
33
+ }
34
+ });
35
+ });
@@ -0,0 +1,5 @@
1
+ import type { H3Event } from 'h3';
2
+ export declare function useDirectusAccessToken(event: H3Event): string | undefined;
3
+ export declare function useDirectusUrl(): string;
4
+ export declare function useDirectus(token?: string): import("@directus/sdk/dist/client-e8d6bf91").D<DirectusCollections> & import("@directus/sdk/dist/login-0506af09").d<DirectusCollections> & import("@directus/sdk/dist/login-0506af09").f<DirectusCollections>;
5
+ export declare function useAdminDirectus(): import("@directus/sdk/dist/client-e8d6bf91").D<DirectusCollections> & import("@directus/sdk/dist/login-0506af09").d<DirectusCollections> & import("@directus/sdk/dist/login-0506af09").f<DirectusCollections>;
@@ -0,0 +1,28 @@
1
+ import { authentication, createDirectus, rest } from "@directus/sdk";
2
+ import { getCookie } from "h3";
3
+ import { useRuntimeConfig } from "#imports";
4
+ export function useDirectusAccessToken(event) {
5
+ return getCookie(event, "directus_access_token");
6
+ }
7
+ export function useDirectusUrl() {
8
+ return useRuntimeConfig().public.rolley.url;
9
+ }
10
+ export function useDirectus(token) {
11
+ const url = useDirectusUrl();
12
+ if (!url)
13
+ throw new Error("DIRECTUS_URL is not set in config options or .env file");
14
+ const directus = createDirectus(url).with(authentication("json", { autoRefresh: false })).with(rest());
15
+ if (token)
16
+ directus.setToken(token);
17
+ return directus;
18
+ }
19
+ export function useAdminDirectus() {
20
+ const url = useDirectusUrl();
21
+ if (!url)
22
+ throw new Error("DIRECTUS_URL is not set in config options or .env file");
23
+ if (!process.env.DIRECTUS_ADMIN_TOKEN)
24
+ throw new Error("DIRECTUS_ADMIN_TOKEN is not set in config options or .env file");
25
+ const directus = createDirectus(url).with(authentication("json", { autoRefresh: false })).with(rest());
26
+ directus.setToken(process.env.DIRECTUS_ADMIN_TOKEN);
27
+ return directus;
28
+ }
@@ -0,0 +1 @@
1
+ export { useDirectusUrl, useDirectus, useAdminDirectus, useDirectusAccessToken } from './directus';
@@ -0,0 +1 @@
1
+ export { useDirectusUrl, useDirectus, useAdminDirectus, useDirectusAccessToken } from "./directus.mjs";
@@ -0,0 +1,38 @@
1
+ export type DirectusThumbnailFormat = 'jpg' | 'png' | 'webp' | 'tiff'
2
+ export type DirectusThumbnailFit = 'cover' | 'contain' | 'inside' | 'outside'
3
+
4
+ export interface DirectusThumbnailOptions {
5
+ width?: number
6
+ height?: number
7
+ quality?: number
8
+ fit?: DirectusThumbnailFit
9
+ format?: DirectusThumbnailFormat
10
+ withoutEnlargement?: boolean
11
+ token?: string
12
+ }
13
+
14
+ export interface DirectusNotificationObject {
15
+ id?: number
16
+ timestamp?: string
17
+ status?: 'inbox' | 'archived'
18
+ recipient: Array<string> | string
19
+ sender?: Array<string> | string
20
+ subject: string
21
+ message?: string
22
+ collection?: string
23
+ item?: string
24
+ }
25
+
26
+ export interface DirectusQueryParams {
27
+ fields?: Array<string>
28
+ sort?: string | Array<string>
29
+ filter?: Record<string, unknown>
30
+ limit?: number
31
+ offset?: number
32
+ page?: number
33
+ alias?: string | Array<string>
34
+ deep?: Record<string, unknown>
35
+ search?: string
36
+ meta?: 'total_count' | 'filter_count' | '*'
37
+ }
38
+
@@ -0,0 +1,15 @@
1
+
2
+ import { ModuleOptions } from './module'
3
+
4
+ declare module '@nuxt/schema' {
5
+ interface NuxtConfig { ['rolley']?: Partial<ModuleOptions> }
6
+ interface NuxtOptions { ['rolley']?: ModuleOptions }
7
+ }
8
+
9
+ declare module 'nuxt/schema' {
10
+ interface NuxtConfig { ['rolley']?: Partial<ModuleOptions> }
11
+ interface NuxtOptions { ['rolley']?: ModuleOptions }
12
+ }
13
+
14
+
15
+ export { ModuleOptions, default } from './module'
@@ -0,0 +1,15 @@
1
+
2
+ import { ModuleOptions } from './module'
3
+
4
+ declare module '@nuxt/schema' {
5
+ interface NuxtConfig { ['rolley']?: Partial<ModuleOptions> }
6
+ interface NuxtOptions { ['rolley']?: ModuleOptions }
7
+ }
8
+
9
+ declare module 'nuxt/schema' {
10
+ interface NuxtConfig { ['rolley']?: Partial<ModuleOptions> }
11
+ interface NuxtOptions { ['rolley']?: ModuleOptions }
12
+ }
13
+
14
+
15
+ export { ModuleOptions, default } from './module'
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "nuxt-directus-sdk",
3
+ "type": "module",
4
+ "version": "0.0.2",
5
+ "description": "A nuxt module that uses the directus SDK",
6
+ "author": "Matthew Rollinson <matt@rolley.io>",
7
+ "license": "MIT",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/types.d.ts",
11
+ "import": "./dist/module.mjs",
12
+ "require": "./dist/module.cjs"
13
+ }
14
+ },
15
+ "main": "./dist/module.cjs",
16
+ "types": "./dist/types.d.ts",
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "prepack": "nuxt-module-build",
22
+ "dev": "nuxi dev playground",
23
+ "dev:build": "nuxi build playground",
24
+ "dev:prepare": "nuxt-module-build --stub && nuxi prepare playground",
25
+ "release": "npm run lint && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
26
+ "lint": "eslint .",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest watch"
29
+ },
30
+ "dependencies": {
31
+ "@directus/sdk": "^11.0.3",
32
+ "@nuxt/kit": "^3.7.1",
33
+ "change-case": "^4.1.2",
34
+ "defu": "^6.1.1",
35
+ "parse-domain": "^7.0.1",
36
+ "tippy.js": "^6.3.7",
37
+ "ufo": "^1.1.1",
38
+ "vue-toastification": "2.0.0-rc.5"
39
+ },
40
+ "devDependencies": {
41
+ "@nuxt/devtools": "latest",
42
+ "@nuxt/eslint-config": "^0.2.0",
43
+ "@nuxt/module-builder": "^0.5.1",
44
+ "@nuxt/schema": "^3.7.1",
45
+ "@nuxt/test-utils": "^3.7.1",
46
+ "@types/node": "^18.17.14",
47
+ "changelogen": "^0.5.5",
48
+ "nuxt": "^3.7.1",
49
+ "vitest": "^0.34.2"
50
+ },
51
+ "unbuild": {
52
+ "failOnWarn": false
53
+ }
54
+ }