@frigear-nu/authorization 0.0.0-stage → 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.
- package/README.md +10 -2
- package/dist/module.d.mts +58 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +121 -0
- package/dist/runtime/app/components/Can.d.vue.ts +28 -0
- package/dist/runtime/app/components/Can.vue +27 -0
- package/dist/runtime/app/components/Can.vue.d.ts +28 -0
- package/dist/runtime/app/components/Cannot.d.vue.ts +24 -0
- package/dist/runtime/app/components/Cannot.vue +17 -0
- package/dist/runtime/app/components/Cannot.vue.d.ts +24 -0
- package/dist/runtime/app/composables/useAuthorization.d.ts +11 -0
- package/dist/runtime/app/composables/useAuthorization.js +22 -0
- package/dist/runtime/app/middleware/authorization.global.d.ts +2 -0
- package/dist/runtime/app/middleware/authorization.global.js +17 -0
- package/dist/runtime/app/resolver.d.ts +1 -0
- package/dist/runtime/app/resolver.js +3 -0
- package/dist/runtime/server/db/schema.sqlite.d.ts +26 -0
- package/dist/runtime/server/db/schema.sqlite.js +4 -0
- package/dist/runtime/server/middleware/authorization.d.ts +2 -0
- package/dist/runtime/server/middleware/authorization.js +15 -0
- package/dist/runtime/server/resolver.d.ts +2 -0
- package/dist/runtime/server/resolver.js +4 -0
- package/dist/runtime/server/utils/authorization.d.ts +8 -0
- package/dist/runtime/server/utils/authorization.js +21 -0
- package/dist/runtime/utils/ability.d.ts +21 -0
- package/dist/runtime/utils/ability.js +21 -0
- package/dist/runtime/utils/bouncer.d.ts +7 -0
- package/dist/runtime/utils/bouncer.js +17 -0
- package/dist/runtime/utils/error.d.ts +5 -0
- package/dist/runtime/utils/error.js +11 -0
- package/dist/runtime/utils/index.d.ts +5 -0
- package/dist/runtime/utils/index.js +5 -0
- package/dist/runtime/utils/rules.d.ts +20 -0
- package/dist/runtime/utils/rules.js +48 -0
- package/dist/runtime/utils/types.d.ts +37 -0
- package/dist/runtime/utils/types.js +1 -0
- package/dist/types.d.mts +5 -0
- package/package.json +72 -3
package/README.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @frigear-nu/authorization
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Policy-based authorization for the frigear ecosystem, built on [`@frigear-nu/users`](../users). Modelled on
|
|
4
|
+
[nuxt-authorization](https://github.com/barbapapazes/nuxt-authorization): define policies with `defineAbility`, the
|
|
5
|
+
current user is injected by a client/server resolver, and check with `authorize`/`allows`/`denies` or `<Can>`.
|
|
6
|
+
|
|
7
|
+
- Adds a `role` column (default `'user'`) to the `users` table - generate and commit a migration in your app.
|
|
8
|
+
- Route rules as keys on Nuxt's native `routeRules` (`roles`, `ability`, `guest`, `authenticated`) and per page (`definePageMeta({ authorize })`).
|
|
9
|
+
- Overridable user resolvers (`authorization.resolvers.server` / `.client`).
|
|
10
|
+
|
|
11
|
+
See the [documentation](../../apps/docs/content/4.modules/11.authorization.md) for usage.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { RouteRule, AuthorizeMeta } from '../dist/runtime/utils/types.js';
|
|
3
|
+
export { Ability, AbilityArgs, AuthorizationResponse, AuthorizeMeta, RouteRule } from '../dist/runtime/utils/types.js';
|
|
4
|
+
|
|
5
|
+
interface ModuleOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Path to the file whose exports are the app's abilities (`defineAbility(...)`). Required to refer to
|
|
8
|
+
* abilities by name in `routes`/`definePageMeta`. Defaults to `shared/utils/abilities.ts` (auto-imported in app and server) or `shared/abilities.ts`.
|
|
9
|
+
*/
|
|
10
|
+
abilities?: string;
|
|
11
|
+
/** Override how the user is resolved (paths to default-export functions). Defaults to the session user. */
|
|
12
|
+
resolvers?: {
|
|
13
|
+
/** `(event: H3Event) => User | null | Promise<User | null>` */
|
|
14
|
+
server?: string;
|
|
15
|
+
/** `() => User | null | Promise<User | null>` - runs in the app (client and SSR). */
|
|
16
|
+
client?: string;
|
|
17
|
+
};
|
|
18
|
+
/** Where to send unauthenticated visitors on page navigation. Omit to show a 401 error instead. */
|
|
19
|
+
redirectTo?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Authorization keys that go directly on a `routeRules` entry. Ability names refer to exports of the abilities
|
|
23
|
+
* file and take no arguments.
|
|
24
|
+
*/
|
|
25
|
+
type AuthorizationRouteRules = Omit<RouteRule, 'args' | 'ability'> & {
|
|
26
|
+
ability?: string;
|
|
27
|
+
};
|
|
28
|
+
interface PublicModuleOptions {
|
|
29
|
+
/** The authorization keys of `routeRules`, collected at build time. */
|
|
30
|
+
routes?: Record<string, AuthorizationRouteRules>;
|
|
31
|
+
redirectTo?: string;
|
|
32
|
+
}
|
|
33
|
+
declare module 'nitropack' {
|
|
34
|
+
interface NitroRouteConfig extends AuthorizationRouteRules {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
declare module 'nitropack/types' {
|
|
38
|
+
interface NitroRouteConfig extends AuthorizationRouteRules {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
declare module '#app' {
|
|
42
|
+
interface PageMeta {
|
|
43
|
+
authorize?: AuthorizeMeta;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
declare module 'nuxt/schema' {
|
|
48
|
+
interface NuxtConfig {
|
|
49
|
+
authorization?: ModuleOptions;
|
|
50
|
+
}
|
|
51
|
+
interface PublicRuntimeConfig {
|
|
52
|
+
authorization?: PublicModuleOptions;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
56
|
+
|
|
57
|
+
export { _default as default };
|
|
58
|
+
export type { AuthorizationRouteRules, ModuleOptions, PublicModuleOptions };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { defineNuxtModule, createResolver, addServerTemplate, addTemplate, addTypeTemplate, addImports, addServerImports, addComponentsDir, addServerImportsDir, addRouteMiddleware, addServerHandler } from '@nuxt/kit';
|
|
4
|
+
import { registerModule, extendDatabaseTable } from '@frigear-nu/module-kit/utils';
|
|
5
|
+
import { ROUTE_RULE_KEYS } from '../dist/runtime/utils/types.js';
|
|
6
|
+
|
|
7
|
+
const module$1 = defineNuxtModule({
|
|
8
|
+
meta: { name: "@frigear-nu/authorization", configKey: "authorization" },
|
|
9
|
+
defaults: {
|
|
10
|
+
resolvers: {}
|
|
11
|
+
},
|
|
12
|
+
moduleDependencies: {
|
|
13
|
+
"@nuxthub/core": {
|
|
14
|
+
version: ">=0.10"
|
|
15
|
+
},
|
|
16
|
+
"@frigear-nu/module-kit": {
|
|
17
|
+
version: ">=0.0.1"
|
|
18
|
+
},
|
|
19
|
+
"@frigear-nu/users": {
|
|
20
|
+
version: ">=0.0.1"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
async setup(options, nuxt) {
|
|
24
|
+
registerModule("@frigear-nu/authorization", nuxt);
|
|
25
|
+
const resolver = createResolver(import.meta.url);
|
|
26
|
+
nuxt.options.runtimeConfig.public.authorization = {
|
|
27
|
+
routes: {},
|
|
28
|
+
redirectTo: options.redirectTo
|
|
29
|
+
};
|
|
30
|
+
nuxt.hook("modules:done", () => {
|
|
31
|
+
const routes = {};
|
|
32
|
+
const sources = [nuxt.options.routeRules, nuxt.options.nitro?.routeRules];
|
|
33
|
+
for (const [pattern, rule] of sources.flatMap((source) => Object.entries(source ?? {}))) {
|
|
34
|
+
const picked = Object.fromEntries(ROUTE_RULE_KEYS.filter((key) => rule[key] !== void 0).map((key) => [key, rule[key]]));
|
|
35
|
+
if (Object.keys(picked).length) routes[pattern] = { ...routes[pattern], ...picked };
|
|
36
|
+
}
|
|
37
|
+
nuxt.options.runtimeConfig.public.authorization = { routes, redirectTo: options.redirectTo };
|
|
38
|
+
});
|
|
39
|
+
extendDatabaseTable(nuxt, {
|
|
40
|
+
name: "users",
|
|
41
|
+
resolver,
|
|
42
|
+
file: (dialect) => `./runtime/server/db/schema.${dialect}`
|
|
43
|
+
});
|
|
44
|
+
const resolverSource = async (configured, fallback) => {
|
|
45
|
+
const target = configured ? await resolver.resolvePath(configured, { cwd: nuxt.options.rootDir }) : resolver.resolve(fallback);
|
|
46
|
+
return `export { default } from ${JSON.stringify(target)}`;
|
|
47
|
+
};
|
|
48
|
+
const abilitiesFile = options.abilities ? await resolver.resolvePath(options.abilities, { cwd: nuxt.options.rootDir }) : ["shared/utils/abilities.ts", "shared/abilities.ts"].map((file) => join(nuxt.options.rootDir, file)).find(existsSync);
|
|
49
|
+
const abilitiesSource = abilitiesFile ? `import * as abilities from ${JSON.stringify(abilitiesFile)}
|
|
50
|
+
export { abilities }` : "export const abilities = {}";
|
|
51
|
+
addServerTemplate({
|
|
52
|
+
filename: "#authorization/server-resolver",
|
|
53
|
+
getContents: () => resolverSource(options.resolvers?.server, "./runtime/server/resolver")
|
|
54
|
+
});
|
|
55
|
+
addServerTemplate({
|
|
56
|
+
filename: "#authorization/abilities",
|
|
57
|
+
getContents: () => abilitiesSource
|
|
58
|
+
});
|
|
59
|
+
addTemplate({
|
|
60
|
+
filename: "authorization/client-resolver.mjs",
|
|
61
|
+
write: true,
|
|
62
|
+
getContents: () => resolverSource(options.resolvers?.client, "./runtime/app/resolver")
|
|
63
|
+
});
|
|
64
|
+
addTemplate({
|
|
65
|
+
filename: "authorization/abilities.mjs",
|
|
66
|
+
write: true,
|
|
67
|
+
getContents: () => abilitiesSource
|
|
68
|
+
});
|
|
69
|
+
addTypeTemplate({
|
|
70
|
+
filename: "types/authorization-virtual.d.ts",
|
|
71
|
+
getContents: () => `declare module '#authorization/server-resolver' {
|
|
72
|
+
const resolveServerUser: (event: import('h3').H3Event) => unknown | Promise<unknown>
|
|
73
|
+
export default resolveServerUser
|
|
74
|
+
}
|
|
75
|
+
declare module '#authorization/abilities' {
|
|
76
|
+
export const abilities: Record<string, unknown>
|
|
77
|
+
}
|
|
78
|
+
declare module '#build/authorization/client-resolver.mjs' {
|
|
79
|
+
const resolveClientUser: () => unknown | Promise<unknown>
|
|
80
|
+
export default resolveClientUser
|
|
81
|
+
}
|
|
82
|
+
declare module '#build/authorization/abilities.mjs' {
|
|
83
|
+
export const abilities: Record<string, unknown>
|
|
84
|
+
}
|
|
85
|
+
`
|
|
86
|
+
}, { nitro: true, nuxt: true });
|
|
87
|
+
addTypeTemplate({
|
|
88
|
+
filename: "types/authorization-user.d.ts",
|
|
89
|
+
getContents: () => `declare module '#auth-utils' {
|
|
90
|
+
interface User {
|
|
91
|
+
role: string
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export {}
|
|
95
|
+
`
|
|
96
|
+
}, { nitro: true, nuxt: true });
|
|
97
|
+
const abilityImports = ["defineAbility", "allow", "deny"].map((name) => ({
|
|
98
|
+
name,
|
|
99
|
+
from: resolver.resolve("./runtime/utils/ability")
|
|
100
|
+
}));
|
|
101
|
+
addImports(abilityImports);
|
|
102
|
+
addServerImports(abilityImports);
|
|
103
|
+
addImports(["allows", "denies", "authorize", "useAuthorization"].map((name) => ({
|
|
104
|
+
name,
|
|
105
|
+
from: resolver.resolve("./runtime/app/composables/useAuthorization")
|
|
106
|
+
})));
|
|
107
|
+
addComponentsDir({ path: resolver.resolve("./runtime/app/components") });
|
|
108
|
+
addServerImportsDir(resolver.resolve("./runtime/server/utils"));
|
|
109
|
+
addRouteMiddleware({
|
|
110
|
+
name: "authorization",
|
|
111
|
+
path: resolver.resolve("./runtime/app/middleware/authorization.global"),
|
|
112
|
+
global: true
|
|
113
|
+
});
|
|
114
|
+
addServerHandler({
|
|
115
|
+
middleware: true,
|
|
116
|
+
handler: resolver.resolve("./runtime/server/middleware/authorization")
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
export { module$1 as default };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Ability, AbilityArgs } from '../../utils/index.js';
|
|
2
|
+
declare const __VLS_export: <A extends Ability>(__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: import("vue").PublicProps & __VLS_PrettifyLocal<{
|
|
4
|
+
/** One ability, or several that must all pass. */
|
|
5
|
+
ability: A | A[];
|
|
6
|
+
/** Arguments for the ability (an array of argument arrays when `ability` is an array). */
|
|
7
|
+
args?: AbilityArgs<A> | AbilityArgs<A>[];
|
|
8
|
+
}> & (typeof globalThis extends {
|
|
9
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
10
|
+
} ? P : {});
|
|
11
|
+
expose: (exposed: {}) => void;
|
|
12
|
+
attrs: any;
|
|
13
|
+
slots: {
|
|
14
|
+
default?: (props: {}) => any;
|
|
15
|
+
} & {
|
|
16
|
+
fallback?: (props: {}) => any;
|
|
17
|
+
};
|
|
18
|
+
emit: {};
|
|
19
|
+
}>) => import("vue").VNode & {
|
|
20
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
21
|
+
};
|
|
22
|
+
declare const _default: typeof __VLS_export;
|
|
23
|
+
export default _default;
|
|
24
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
25
|
+
[K in keyof T]: T[K];
|
|
26
|
+
} : {
|
|
27
|
+
[K in keyof T as K]: T[K];
|
|
28
|
+
}) & {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { allows } from "../composables/useAuthorization";
|
|
3
|
+
import { ref, watchEffect } from "#imports";
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
ability: { type: null, required: true },
|
|
6
|
+
args: { type: null, required: false }
|
|
7
|
+
});
|
|
8
|
+
async function resolve() {
|
|
9
|
+
if (Array.isArray(props.ability)) {
|
|
10
|
+
const results = await Promise.all(props.ability.map((ability, index) => allows(ability, ...props.args?.[index] ?? [])));
|
|
11
|
+
return results.every(Boolean);
|
|
12
|
+
}
|
|
13
|
+
return allows(props.ability, ...props.args ?? []);
|
|
14
|
+
}
|
|
15
|
+
const can = ref(await resolve());
|
|
16
|
+
watchEffect(async () => {
|
|
17
|
+
can.value = await resolve();
|
|
18
|
+
});
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<slot v-if="can" />
|
|
23
|
+
<slot
|
|
24
|
+
v-else
|
|
25
|
+
name="fallback"
|
|
26
|
+
/>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Ability, AbilityArgs } from '../../utils/index.js';
|
|
2
|
+
declare const __VLS_export: <A extends Ability>(__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: import("vue").PublicProps & __VLS_PrettifyLocal<{
|
|
4
|
+
/** One ability, or several that must all pass. */
|
|
5
|
+
ability: A | A[];
|
|
6
|
+
/** Arguments for the ability (an array of argument arrays when `ability` is an array). */
|
|
7
|
+
args?: AbilityArgs<A> | AbilityArgs<A>[];
|
|
8
|
+
}> & (typeof globalThis extends {
|
|
9
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
10
|
+
} ? P : {});
|
|
11
|
+
expose: (exposed: {}) => void;
|
|
12
|
+
attrs: any;
|
|
13
|
+
slots: {
|
|
14
|
+
default?: (props: {}) => any;
|
|
15
|
+
} & {
|
|
16
|
+
fallback?: (props: {}) => any;
|
|
17
|
+
};
|
|
18
|
+
emit: {};
|
|
19
|
+
}>) => import("vue").VNode & {
|
|
20
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
21
|
+
};
|
|
22
|
+
declare const _default: typeof __VLS_export;
|
|
23
|
+
export default _default;
|
|
24
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
25
|
+
[K in keyof T]: T[K];
|
|
26
|
+
} : {
|
|
27
|
+
[K in keyof T as K]: T[K];
|
|
28
|
+
}) & {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Ability, AbilityArgs } from '../../utils/index.js';
|
|
2
|
+
declare const __VLS_export: <A extends Ability>(__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: import("vue").PublicProps & __VLS_PrettifyLocal<{
|
|
4
|
+
ability: A;
|
|
5
|
+
args?: AbilityArgs<A>;
|
|
6
|
+
}> & (typeof globalThis extends {
|
|
7
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
8
|
+
} ? P : {});
|
|
9
|
+
expose: (exposed: {}) => void;
|
|
10
|
+
attrs: any;
|
|
11
|
+
slots: {
|
|
12
|
+
default?: (props: {}) => any;
|
|
13
|
+
};
|
|
14
|
+
emit: {};
|
|
15
|
+
}>) => import("vue").VNode & {
|
|
16
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
17
|
+
};
|
|
18
|
+
declare const _default: typeof __VLS_export;
|
|
19
|
+
export default _default;
|
|
20
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
21
|
+
[K in keyof T]: T[K];
|
|
22
|
+
} : {
|
|
23
|
+
[K in keyof T as K]: T[K];
|
|
24
|
+
}) & {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { allows } from "../composables/useAuthorization";
|
|
3
|
+
import { ref, watchEffect } from "#imports";
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
ability: { type: null, required: true },
|
|
6
|
+
args: { type: null, required: false }
|
|
7
|
+
});
|
|
8
|
+
const resolve = async () => !await allows(props.ability, ...props.args ?? []);
|
|
9
|
+
const cannot = ref(await resolve());
|
|
10
|
+
watchEffect(async () => {
|
|
11
|
+
cannot.value = await resolve();
|
|
12
|
+
});
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<slot v-if="cannot" />
|
|
17
|
+
</template>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Ability, AbilityArgs } from '../../utils/index.js';
|
|
2
|
+
declare const __VLS_export: <A extends Ability>(__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: import("vue").PublicProps & __VLS_PrettifyLocal<{
|
|
4
|
+
ability: A;
|
|
5
|
+
args?: AbilityArgs<A>;
|
|
6
|
+
}> & (typeof globalThis extends {
|
|
7
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
8
|
+
} ? P : {});
|
|
9
|
+
expose: (exposed: {}) => void;
|
|
10
|
+
attrs: any;
|
|
11
|
+
slots: {
|
|
12
|
+
default?: (props: {}) => any;
|
|
13
|
+
};
|
|
14
|
+
emit: {};
|
|
15
|
+
}>) => import("vue").VNode & {
|
|
16
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
17
|
+
};
|
|
18
|
+
declare const _default: typeof __VLS_export;
|
|
19
|
+
export default _default;
|
|
20
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
21
|
+
[K in keyof T]: T[K];
|
|
22
|
+
} : {
|
|
23
|
+
[K in keyof T as K]: T[K];
|
|
24
|
+
}) & {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Ability, AbilityArgs } from '../../utils/index.js';
|
|
2
|
+
/** Check whether the current (client-resolved) user may perform an action. */
|
|
3
|
+
export declare function allows<A extends Ability>(ability: A, ...args: AbilityArgs<A>): Promise<boolean>;
|
|
4
|
+
export declare function denies<A extends Ability>(ability: A, ...args: AbilityArgs<A>): Promise<boolean>;
|
|
5
|
+
/** Throws a Nuxt error (403 by default) when the current user may not perform the action. */
|
|
6
|
+
export declare function authorize<A extends Ability>(ability: A, ...args: AbilityArgs<A>): Promise<void>;
|
|
7
|
+
export declare function useAuthorization(): {
|
|
8
|
+
allows: typeof allows;
|
|
9
|
+
denies: typeof denies;
|
|
10
|
+
authorize: typeof authorize;
|
|
11
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { allows as _allows, denies as _denies, authorize as _authorize, AuthorizationError } from "../../utils/index.js";
|
|
2
|
+
import resolveClientUser from "#build/authorization/client-resolver.mjs";
|
|
3
|
+
import { createError } from "#imports";
|
|
4
|
+
export async function allows(ability, ...args) {
|
|
5
|
+
return _allows(ability, await resolveClientUser(), ...args);
|
|
6
|
+
}
|
|
7
|
+
export async function denies(ability, ...args) {
|
|
8
|
+
return _denies(ability, await resolveClientUser(), ...args);
|
|
9
|
+
}
|
|
10
|
+
export async function authorize(ability, ...args) {
|
|
11
|
+
try {
|
|
12
|
+
await _authorize(ability, await resolveClientUser(), ...args);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
if (error instanceof AuthorizationError) {
|
|
15
|
+
throw createError({ statusCode: error.statusCode, message: error.message });
|
|
16
|
+
}
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function useAuthorization() {
|
|
21
|
+
return { allows, denies, authorize };
|
|
22
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { evaluateRule, normalizeAuthorizeMeta, resolveRule } from "../../utils/index.js";
|
|
2
|
+
import { abilities } from "#build/authorization/abilities.mjs";
|
|
3
|
+
import resolveClientUser from "#build/authorization/client-resolver.mjs";
|
|
4
|
+
import { abortNavigation, createError, defineNuxtRouteMiddleware, navigateTo, useRuntimeConfig } from "#imports";
|
|
5
|
+
export default defineNuxtRouteMiddleware(async (to) => {
|
|
6
|
+
const config = useRuntimeConfig().public.authorization;
|
|
7
|
+
const meta = to.meta.authorize;
|
|
8
|
+
const rule = meta ? normalizeAuthorizeMeta(meta) : resolveRule(config?.routes, to.path);
|
|
9
|
+
if (!rule) return;
|
|
10
|
+
const user = await resolveClientUser();
|
|
11
|
+
const response = await evaluateRule(rule, user, abilities);
|
|
12
|
+
if (response.authorized) return;
|
|
13
|
+
if (response.statusCode === 401 && config?.redirectTo) {
|
|
14
|
+
return navigateTo(config.redirectTo);
|
|
15
|
+
}
|
|
16
|
+
return abortNavigation(createError({ statusCode: response.statusCode ?? 403, message: response.message }));
|
|
17
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function resolveClientUser(): any;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const users: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
|
|
2
|
+
name: "users";
|
|
3
|
+
schema: undefined;
|
|
4
|
+
columns: {
|
|
5
|
+
role: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
6
|
+
name: "role";
|
|
7
|
+
tableName: "users";
|
|
8
|
+
dataType: "string";
|
|
9
|
+
columnType: "SQLiteText";
|
|
10
|
+
data: string;
|
|
11
|
+
driverParam: string;
|
|
12
|
+
notNull: true;
|
|
13
|
+
hasDefault: true;
|
|
14
|
+
isPrimaryKey: false;
|
|
15
|
+
isAutoincrement: false;
|
|
16
|
+
hasRuntimeDefault: false;
|
|
17
|
+
enumValues: [string, ...string[]];
|
|
18
|
+
baseColumn: never;
|
|
19
|
+
identity: undefined;
|
|
20
|
+
generated: undefined;
|
|
21
|
+
}, {}, {
|
|
22
|
+
length: number | undefined;
|
|
23
|
+
}>;
|
|
24
|
+
};
|
|
25
|
+
dialect: "sqlite";
|
|
26
|
+
}>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineEventHandler, createError, getRequestURL } from "h3";
|
|
2
|
+
import { evaluateRule, resolveRule } from "../../utils/index.js";
|
|
3
|
+
import { abilities } from "#authorization/abilities";
|
|
4
|
+
import resolveServerUser from "#authorization/server-resolver";
|
|
5
|
+
import { useRuntimeConfig } from "#imports";
|
|
6
|
+
export default defineEventHandler(async (event) => {
|
|
7
|
+
const { pathname } = getRequestURL(event);
|
|
8
|
+
if (!pathname.startsWith("/api/")) return;
|
|
9
|
+
const rule = resolveRule(useRuntimeConfig(event).public.authorization?.routes, pathname);
|
|
10
|
+
if (!rule) return;
|
|
11
|
+
const response = await evaluateRule(rule, await resolveServerUser(event), abilities);
|
|
12
|
+
if (!response.authorized) {
|
|
13
|
+
throw createError({ statusCode: response.statusCode ?? 403, message: response.message });
|
|
14
|
+
}
|
|
15
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
import type { Ability, AbilityArgs } from '../../utils/index.js';
|
|
3
|
+
/** Resolve the current user with the configured server resolver (session user by default). */
|
|
4
|
+
export declare function resolveAuthorizationUser(event: H3Event): any;
|
|
5
|
+
export declare function allows<A extends Ability>(event: H3Event, ability: A, ...args: AbilityArgs<A>): Promise<boolean>;
|
|
6
|
+
export declare function denies<A extends Ability>(event: H3Event, ability: A, ...args: AbilityArgs<A>): Promise<boolean>;
|
|
7
|
+
/** Throws an h3 error (401 for guests, 403 otherwise) when the user may not perform the action. */
|
|
8
|
+
export declare function authorize<A extends Ability>(event: H3Event, ability: A, ...args: AbilityArgs<A>): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { allows as _allows, denies as _denies, authorize as _authorize, AuthorizationError } from "../../utils/index.js";
|
|
2
|
+
import resolveServerUser from "#authorization/server-resolver";
|
|
3
|
+
export function resolveAuthorizationUser(event) {
|
|
4
|
+
return resolveServerUser(event);
|
|
5
|
+
}
|
|
6
|
+
export async function allows(event, ability, ...args) {
|
|
7
|
+
return _allows(ability, await resolveServerUser(event), ...args);
|
|
8
|
+
}
|
|
9
|
+
export async function denies(event, ability, ...args) {
|
|
10
|
+
return _denies(ability, await resolveServerUser(event), ...args);
|
|
11
|
+
}
|
|
12
|
+
export async function authorize(event, ability, ...args) {
|
|
13
|
+
try {
|
|
14
|
+
await _authorize(ability, await resolveServerUser(event), ...args);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error instanceof AuthorizationError) {
|
|
17
|
+
throw createError({ statusCode: error.statusCode, message: error.message });
|
|
18
|
+
}
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Ability, AuthorizationResponse, Authorizer, AuthorizerResponse } from './types.js';
|
|
2
|
+
type AbilityFromAuthorizer<A> = A extends (user: infer User, ...args: any[]) => AuthorizerResponse ? Ability<User> : never;
|
|
3
|
+
/**
|
|
4
|
+
* Define a policy. The resolved user is injected as the first argument; guests (no user) are denied
|
|
5
|
+
* before the policy runs unless `allowGuest` is set, in which case the policy receives `null`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* export const editPost = defineAbility((user: User, post: Post) => user.id === post.userId)
|
|
9
|
+
* export const viewFeed = defineAbility({ allowGuest: true }, (user: User | null) => true)
|
|
10
|
+
*/
|
|
11
|
+
export declare function defineAbility<A extends Authorizer>(authorizer: A): AbilityFromAuthorizer<A> & Ability;
|
|
12
|
+
export declare function defineAbility<A extends Authorizer>(options: {
|
|
13
|
+
allowGuest: boolean;
|
|
14
|
+
}, authorizer: A): AbilityFromAuthorizer<A> & Ability;
|
|
15
|
+
export declare function allow(): AuthorizationResponse;
|
|
16
|
+
export declare function deny(options?: {
|
|
17
|
+
statusCode?: number;
|
|
18
|
+
message?: string;
|
|
19
|
+
}): AuthorizationResponse;
|
|
20
|
+
export declare function isAbility(value: unknown): value is Ability;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function defineAbility(optionsOrAuthorizer, maybeAuthorizer) {
|
|
2
|
+
const allowGuest = typeof optionsOrAuthorizer === "function" ? false : optionsOrAuthorizer.allowGuest;
|
|
3
|
+
const original = typeof optionsOrAuthorizer === "function" ? optionsOrAuthorizer : maybeAuthorizer;
|
|
4
|
+
return {
|
|
5
|
+
allowGuest,
|
|
6
|
+
original,
|
|
7
|
+
execute(user, ...args) {
|
|
8
|
+
if (user === null && !allowGuest) return { authorized: false };
|
|
9
|
+
return original(user, ...args);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function allow() {
|
|
14
|
+
return { authorized: true };
|
|
15
|
+
}
|
|
16
|
+
export function deny(options = {}) {
|
|
17
|
+
return { authorized: false, ...options };
|
|
18
|
+
}
|
|
19
|
+
export function isAbility(value) {
|
|
20
|
+
return typeof value === "object" && value !== null && typeof value.execute === "function";
|
|
21
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Ability, AbilityArgs, AuthorizationResponse } from './types.js';
|
|
2
|
+
export declare function normalizeResponse(result: boolean | AuthorizationResponse): AuthorizationResponse;
|
|
3
|
+
/** Run an ability for an already-resolved user. Used by the client and server helpers. */
|
|
4
|
+
export declare function check<A extends Ability>(ability: A, user: unknown, ...args: AbilityArgs<A>): Promise<AuthorizationResponse>;
|
|
5
|
+
export declare function allows<A extends Ability>(ability: A, user: unknown, ...args: AbilityArgs<A>): Promise<boolean>;
|
|
6
|
+
export declare function denies<A extends Ability>(ability: A, user: unknown, ...args: AbilityArgs<A>): Promise<boolean>;
|
|
7
|
+
export declare function authorize<A extends Ability>(ability: A, user: unknown, ...args: AbilityArgs<A>): Promise<void>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createAuthorizationError } from "./error.js";
|
|
2
|
+
export function normalizeResponse(result) {
|
|
3
|
+
return typeof result === "boolean" ? { authorized: result } : result;
|
|
4
|
+
}
|
|
5
|
+
export async function check(ability, user, ...args) {
|
|
6
|
+
return normalizeResponse(await ability.execute(user ?? null, ...args));
|
|
7
|
+
}
|
|
8
|
+
export async function allows(ability, user, ...args) {
|
|
9
|
+
return (await check(ability, user, ...args)).authorized;
|
|
10
|
+
}
|
|
11
|
+
export async function denies(ability, user, ...args) {
|
|
12
|
+
return !await allows(ability, user, ...args);
|
|
13
|
+
}
|
|
14
|
+
export async function authorize(ability, user, ...args) {
|
|
15
|
+
const response = await check(ability, user, ...args);
|
|
16
|
+
if (!response.authorized) throw createAuthorizationError(response.message, response.statusCode);
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export class AuthorizationError extends Error {
|
|
2
|
+
statusCode;
|
|
3
|
+
constructor(message, statusCode) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "AuthorizationError";
|
|
6
|
+
this.statusCode = statusCode;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function createAuthorizationError(message = "Unauthorized", statusCode = 403) {
|
|
10
|
+
return new AuthorizationError(message, statusCode);
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AuthorizationResponse, AuthorizeMeta, RouteRule } from './types.js';
|
|
2
|
+
export type AbilityRegistry = Record<string, unknown>;
|
|
3
|
+
/**
|
|
4
|
+
* Match a route path against a pattern: `/admin/**` (prefix), `*` (single segment) or an exact path.
|
|
5
|
+
*/
|
|
6
|
+
export declare function matchRoute(pattern: string, path: string): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the rule for a path from `routeRules`-style entries. Like Nitro's route rules, every matching pattern
|
|
9
|
+
* contributes and keys of more specific patterns override less specific ones. Returns `undefined` when nothing
|
|
10
|
+
* applies, including when the merged result says `authenticated: false`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveRule(routes: Record<string, RouteRule> | undefined, path: string): RouteRule | undefined;
|
|
13
|
+
export declare function normalizeAuthorizeMeta(meta: AuthorizeMeta): RouteRule;
|
|
14
|
+
/**
|
|
15
|
+
* Evaluate a declarative route rule for an already-resolved user.
|
|
16
|
+
* Unauthenticated visitors get 401 (unless `guest` is set), role/ability mismatches get 403.
|
|
17
|
+
*/
|
|
18
|
+
export declare function evaluateRule(rule: RouteRule, user: {
|
|
19
|
+
role?: string;
|
|
20
|
+
} | null, abilities?: AbilityRegistry): Promise<AuthorizationResponse>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { isAbility } from "./ability.js";
|
|
2
|
+
import { normalizeResponse } from "./bouncer.js";
|
|
3
|
+
export function matchRoute(pattern, path) {
|
|
4
|
+
const normalize = (value) => value.replace(/\/+$/, "") || "/";
|
|
5
|
+
const p = normalize(pattern);
|
|
6
|
+
const s = normalize(path);
|
|
7
|
+
if (p.endsWith("/**")) {
|
|
8
|
+
const base = p.slice(0, -3);
|
|
9
|
+
return s === base || s.startsWith(`${base}/`);
|
|
10
|
+
}
|
|
11
|
+
const patternSegments = p.split("/");
|
|
12
|
+
const pathSegments = s.split("/");
|
|
13
|
+
return patternSegments.length === pathSegments.length && patternSegments.every((segment, index) => segment === "*" || segment === pathSegments[index]);
|
|
14
|
+
}
|
|
15
|
+
function specificity(pattern) {
|
|
16
|
+
const segments = pattern.split("/").filter(Boolean);
|
|
17
|
+
const literals = segments.filter((segment) => segment !== "*" && segment !== "**").length;
|
|
18
|
+
return literals * 10 + (pattern.endsWith("/**") ? 0 : 5);
|
|
19
|
+
}
|
|
20
|
+
export function resolveRule(routes, path) {
|
|
21
|
+
const matches = Object.entries(routes ?? {}).filter(([pattern]) => matchRoute(pattern, path)).sort(([a], [b]) => specificity(a) - specificity(b));
|
|
22
|
+
if (!matches.length) return void 0;
|
|
23
|
+
const rule = Object.assign({}, ...matches.map(([, entry]) => entry));
|
|
24
|
+
return rule.authenticated === false ? void 0 : rule;
|
|
25
|
+
}
|
|
26
|
+
export function normalizeAuthorizeMeta(meta) {
|
|
27
|
+
if (typeof meta === "string") return { ability: meta };
|
|
28
|
+
if (isAbility(meta)) return { ability: meta };
|
|
29
|
+
return meta;
|
|
30
|
+
}
|
|
31
|
+
export async function evaluateRule(rule, user, abilities = {}) {
|
|
32
|
+
if (rule.authenticated === false) return { authorized: true };
|
|
33
|
+
if (!user && !rule.guest) {
|
|
34
|
+
return { authorized: false, statusCode: 401, message: "Authentication required" };
|
|
35
|
+
}
|
|
36
|
+
if (rule.roles?.length && (!user?.role || !rule.roles.includes(user.role))) {
|
|
37
|
+
return { authorized: false, statusCode: 403, message: "Forbidden" };
|
|
38
|
+
}
|
|
39
|
+
if (rule.ability) {
|
|
40
|
+
const ability = typeof rule.ability === "string" ? abilities[rule.ability] : rule.ability;
|
|
41
|
+
if (!isAbility(ability)) {
|
|
42
|
+
throw new Error(`[@frigear-nu/authorization] Unknown ability "${String(rule.ability)}". Export it from your abilities file.`);
|
|
43
|
+
}
|
|
44
|
+
const response = normalizeResponse(await ability.execute(user, ...rule.args ?? []));
|
|
45
|
+
if (!response.authorized) return { statusCode: 403, message: "Forbidden", ...response };
|
|
46
|
+
}
|
|
47
|
+
return { authorized: true };
|
|
48
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detailed outcome of an ability check. Lets an ability customise the error that is thrown on denial.
|
|
3
|
+
*/
|
|
4
|
+
export interface AuthorizationResponse {
|
|
5
|
+
authorized: boolean;
|
|
6
|
+
statusCode?: number;
|
|
7
|
+
message?: string;
|
|
8
|
+
}
|
|
9
|
+
export type AuthorizerResponse = boolean | AuthorizationResponse | Promise<boolean | AuthorizationResponse>;
|
|
10
|
+
/** A policy function: receives the resolved user first, then any call-site arguments. */
|
|
11
|
+
export type Authorizer<User = any> = (user: User, ...args: any[]) => AuthorizerResponse;
|
|
12
|
+
export interface Ability<User = any> {
|
|
13
|
+
allowGuest: boolean;
|
|
14
|
+
original: Authorizer<User>;
|
|
15
|
+
execute: (user: User | null, ...args: any[]) => AuthorizerResponse;
|
|
16
|
+
}
|
|
17
|
+
/** Call-site arguments of an ability, inferred from the policy signature. */
|
|
18
|
+
export type AbilityArgs<A extends Ability> = A extends {
|
|
19
|
+
original: (user: any, ...args: infer Args) => AuthorizerResponse;
|
|
20
|
+
} ? Args : never;
|
|
21
|
+
/** Declarative rule: the keys of a `routeRules` entry, or the object form of `definePageMeta({ authorize })`. */
|
|
22
|
+
export interface RouteRule {
|
|
23
|
+
/** Allowed roles (matched against `user.role`). */
|
|
24
|
+
roles?: string[];
|
|
25
|
+
/** Name of an exported ability from the abilities file, or (page meta only) an ability object. */
|
|
26
|
+
ability?: string | Ability;
|
|
27
|
+
/** Arguments passed to the ability (page meta only - config rules cannot carry arguments). */
|
|
28
|
+
args?: unknown[];
|
|
29
|
+
/** Let unauthenticated visitors through (the ability/roles checks still apply). */
|
|
30
|
+
guest?: boolean;
|
|
31
|
+
/** `true`: require a logged-in user. `false`: no authorization at all (opens a sub-route of a protected pattern). */
|
|
32
|
+
authenticated?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/** The authorization keys that go directly on a `routeRules` entry. */
|
|
35
|
+
export declare const ROUTE_RULE_KEYS: readonly ["roles", "ability", "guest", "authenticated"];
|
|
36
|
+
/** What `definePageMeta({ authorize })` accepts. */
|
|
37
|
+
export type AuthorizeMeta = string | Ability | RouteRule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const ROUTE_RULE_KEYS = ["roles", "ability", "guest", "authenticated"];
|
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { type Ability, type AbilityArgs, type AuthorizationResponse, type AuthorizeMeta, type RouteRule } from '../dist/runtime/utils/types.js'
|
|
2
|
+
|
|
3
|
+
export { default } from './module.mjs'
|
|
4
|
+
|
|
5
|
+
export { type AuthorizationRouteRules, type ModuleOptions, type PublicModuleOptions } from './module.mjs'
|
package/package.json
CHANGED
|
@@ -1,6 +1,75 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frigear-nu/authorization",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Policy-based authorization (abilities, resolvers, route rules, <Can>) for the frigear ecosystem, built on @frigear-nu/users.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/frigear-nu/frigear-nu.git",
|
|
8
|
+
"directory": "packages/authorization"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/module.mjs",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/types.d.mts",
|
|
16
|
+
"import": "./dist/module.mjs",
|
|
17
|
+
"default": "./dist/module.mjs"
|
|
18
|
+
},
|
|
19
|
+
"./utils": {
|
|
20
|
+
"types": "./dist/runtime/utils/index.d.ts",
|
|
21
|
+
"import": "./dist/runtime/utils/index.js",
|
|
22
|
+
"default": "./dist/runtime/utils/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"workspaces": [
|
|
29
|
+
"playground"
|
|
30
|
+
],
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@frigear-nu/module-kit": "0.1.3",
|
|
33
|
+
"@frigear-nu/users": "1.0.5",
|
|
34
|
+
"@libsql/client": "^0.18.0",
|
|
35
|
+
"@nuxt/kit": "^4.6.0",
|
|
36
|
+
"drizzle-kit": "^0.31.11",
|
|
37
|
+
"drizzle-orm": "^0.45.3"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@nuxthub/core": "^0.10.8"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@nuxt/devtools": "^3.4.2",
|
|
44
|
+
"@nuxt/eslint-config": "^1.17.0",
|
|
45
|
+
"@nuxt/module-builder": "^1.0.3",
|
|
46
|
+
"@nuxt/schema": "^4.6.0",
|
|
47
|
+
"@nuxt/test-utils": "^4.3.2",
|
|
48
|
+
"@nuxthub/core": "^0.10.8",
|
|
49
|
+
"@types/node": "latest",
|
|
50
|
+
"eslint": "^10.10.0",
|
|
51
|
+
"nuxt": "^4.6.0",
|
|
52
|
+
"typescript": "^6.0.3",
|
|
53
|
+
"vitest": "^5.0.1",
|
|
54
|
+
"vue-tsc": "^3.3.11"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"postinstall": "nuxt-module-build build --stub && nuxt prepare",
|
|
58
|
+
"build": "nuxt-module-build build",
|
|
59
|
+
"dev": "npm run dev:prepare && nuxt dev playground",
|
|
60
|
+
"dev:build": "nuxt build playground",
|
|
61
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
|
|
62
|
+
"lint": "eslint .",
|
|
63
|
+
"lint:fix": "eslint . --fix",
|
|
64
|
+
"test": "vitest run",
|
|
65
|
+
"test:watch": "vitest watch",
|
|
66
|
+
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
67
|
+
},
|
|
68
|
+
"typesVersions": {
|
|
69
|
+
"*": {
|
|
70
|
+
".": [
|
|
71
|
+
"./dist/types.d.mts"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
6
75
|
}
|