@bg-dev/nuxt-zenstack 0.0.1 → 0.0.3

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 (36) hide show
  1. package/dist/module.d.mts +2 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +26 -20
  4. package/dist/runtime/composables/common.d.ts +19 -0
  5. package/dist/runtime/composables/common.js +12 -0
  6. package/dist/runtime/composables/index.d.ts +6 -0
  7. package/dist/runtime/composables/index.js +6 -0
  8. package/dist/runtime/composables/useZenstackCreate/index.d.ts +10 -0
  9. package/dist/runtime/composables/useZenstackCreate/index.js +40 -0
  10. package/dist/runtime/composables/useZenstackDelete/index.d.ts +10 -0
  11. package/dist/runtime/composables/useZenstackDelete/index.js +38 -0
  12. package/dist/runtime/composables/useZenstackRead/index.d.ts +30 -0
  13. package/dist/runtime/composables/useZenstackRead/index.js +70 -0
  14. package/dist/runtime/composables/useZenstackReadMany/index.d.ts +50 -0
  15. package/dist/runtime/composables/useZenstackReadMany/index.js +75 -0
  16. package/dist/runtime/composables/useZenstackStore/helpers.d.ts +5 -0
  17. package/dist/runtime/composables/useZenstackStore/helpers.js +16 -0
  18. package/dist/runtime/composables/useZenstackStore/index.d.ts +46 -0
  19. package/dist/runtime/composables/useZenstackStore/index.js +71 -0
  20. package/dist/runtime/composables/useZenstackStore/normalization.d.ts +5 -0
  21. package/dist/runtime/composables/useZenstackStore/normalization.js +31 -0
  22. package/dist/runtime/composables/useZenstackUpdate/index.d.ts +10 -0
  23. package/dist/runtime/composables/useZenstackUpdate/index.js +40 -0
  24. package/dist/runtime/server/api/{[model] → models/[model]}/[id].delete.js +2 -2
  25. package/dist/runtime/server/api/{[model] → models/[model]}/[id].get.js +2 -2
  26. package/dist/runtime/server/api/{[model] → models/[model]}/[id].patch.js +4 -2
  27. package/dist/runtime/server/api/{[model] → models/[model]}/index.get.js +2 -2
  28. package/dist/runtime/server/api/{[model] → models/[model]}/index.post.js +5 -3
  29. package/dist/runtime/server/utils/index.d.ts +4 -3
  30. package/dist/runtime/server/utils/index.js +25 -4
  31. package/package.json +5 -1
  32. /package/dist/runtime/server/api/{[model] → models/[model]}/[id].delete.d.ts +0 -0
  33. /package/dist/runtime/server/api/{[model] → models/[model]}/[id].get.d.ts +0 -0
  34. /package/dist/runtime/server/api/{[model] → models/[model]}/[id].patch.d.ts +0 -0
  35. /package/dist/runtime/server/api/{[model] → models/[model]}/index.get.d.ts +0 -0
  36. /package/dist/runtime/server/api/{[model] → models/[model]}/index.post.d.ts +0 -0
package/dist/module.d.mts CHANGED
@@ -1,7 +1,9 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
+ import { FetchPolicy } from '../dist/runtime/composables/common.js';
2
3
 
3
4
  interface ModuleOptions {
4
5
  apiPath: string;
6
+ fetchPolicy: FetchPolicy;
5
7
  }
6
8
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
7
9
 
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bg-dev/nuxt-zenstack",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "configKey": "zenstack",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
package/dist/module.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import { defineNuxtModule, createResolver, addServerImports, addTemplate, addServerTemplate, addTypeTemplate, addServerHandler } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, addServerImports, addTemplate, addServerTemplate, addTypeTemplate, addServerHandler, addImportsDir } from '@nuxt/kit';
2
2
  import { joinURL } from 'ufo';
3
3
  import { defu } from 'defu';
4
4
 
5
5
  const name = "@bg-dev/nuxt-zenstack";
6
- const version = "0.0.1";
6
+ const version = "0.0.3";
7
7
 
8
8
  const module$1 = defineNuxtModule({
9
9
  meta: {
@@ -13,19 +13,21 @@ const module$1 = defineNuxtModule({
13
13
  },
14
14
  // Default configuration options of the Nuxt module
15
15
  defaults: {
16
- apiPath: "/api/models"
16
+ apiPath: "/api/_zenstack",
17
+ fetchPolicy: "cache-first"
17
18
  },
18
19
  setup(_options, _nuxt) {
19
20
  _nuxt.options.runtimeConfig.public = defu(_nuxt.options.runtimeConfig.public, {
20
21
  zenstack: {
21
- apiPath: _options.apiPath
22
+ apiPath: _options.apiPath,
23
+ fetchPolicy: _options.fetchPolicy
22
24
  }
23
25
  });
24
26
  const resolver = createResolver(import.meta.url);
25
27
  addServerImports({
26
28
  from: resolver.resolve("./runtime/server/utils"),
27
29
  name: "provideClient",
28
- as: "provideZenStackClient"
30
+ as: "provideZenstackClient"
29
31
  });
30
32
  addTemplate({
31
33
  write: true,
@@ -45,40 +47,44 @@ export { schema as zenstackSchema } from '~~/zenstack/schema'
45
47
  getContents: () => `
46
48
  import type { ModelOperations, ModelResult, IncludeInput, WhereInput, FindManyArgs, SimplifiedPlainResult, SelectIncludeOmit, QueryOptions, CreateArgs, UpdateArgs } from '@zenstackhq/orm'
47
49
  import type { SchemaType } from '~~/zenstack/schema'
50
+ import type { ModelDef } from '@zenstackhq/orm/schema'
51
+
48
52
  type ItemGetPayload<Zmodel extends $Zmodel, Args extends SelectIncludeOmit<SchemaType, Zmodel, true>, Options extends QueryOptions<SchemaType> = QueryOptions<SchemaType>> = SimplifiedPlainResult<SchemaType, Zmodel, Args, Options>
53
+
49
54
  export type $Zschema = SchemaType
50
55
  export type $Zmodel = keyof SchemaType['models']
56
+ export type $Zdef = ModelDef
51
57
  export type $Zoperations<Zmodel extends $Zmodel> = ModelOperations<SchemaType, Zmodel>
52
58
  export type $Zid<Zmodel extends $Zmodel> = ModelResult<SchemaType, Zmodel> extends { id: infer Id } ? Id : never
53
- export type $Zinclude<Zmodel extends $Zmodel> = IncludeInput<SchemaType, Zmodel>
54
- export type $Zitem<Zmodel extends $Zmodel, Zinclude extends $Zinclude<Zmodel> = never> = ItemGetPayload<Zmodel, { include: Zinclude }>
55
- export type $Zwhere<Zmodel extends $Zmodel> = WhereInput<SchemaType, Zmodel>
56
- export type $ZorderBy<Zmodel extends $Zmodel> = FindManyArgs<SchemaType, Zmodel>['orderBy']
59
+ export type $Zinclude<Zmodel extends $Zmodel> = IncludeInput<SchemaType, Zmodel> | undefined
60
+ export type $Zitem<Zmodel extends $Zmodel, Zinclude extends $Zinclude = undefined> = ItemGetPayload<Zmodel, { include: Zinclude }>
61
+ export type $Zwhere<Zmodel extends $Zmodel> = WhereInput<SchemaType, Zmodel> | undefined
62
+ export type $ZorderBy<Zmodel extends $Zmodel> = FindManyArgs<SchemaType, Zmodel>['orderBy'] | undefined
57
63
  export type $ZcreateData<Zmodel extends $Zmodel> = CreateArgs<SchemaType, Zmodel>['data']
58
64
  export type $ZupdateData<Zmodel extends $Zmodel> = UpdateArgs<SchemaType, Zmodel>['data']
59
- export type $Zerror = {message: string, reason: string, rejectedByValidation: boolean, model: string}
60
65
  `
61
66
  });
62
67
  addServerHandler({
63
- route: joinURL(_options.apiPath, "/:model"),
64
- handler: resolver.resolve("./runtime/server/api/[model]/index.get")
68
+ route: joinURL(_options.apiPath, "models/:model"),
69
+ handler: resolver.resolve("./runtime/server/api/models/[model]/index.get")
65
70
  });
66
71
  addServerHandler({
67
- route: joinURL(_options.apiPath, "/:model"),
68
- handler: resolver.resolve("./runtime/server/api/[model]/index.post")
72
+ route: joinURL(_options.apiPath, "models/:model"),
73
+ handler: resolver.resolve("./runtime/server/api/models/[model]/index.post")
69
74
  });
70
75
  addServerHandler({
71
- route: joinURL(_options.apiPath, "/:model/:id"),
72
- handler: resolver.resolve("./runtime/server/api/[model]/[id].patch")
76
+ route: joinURL(_options.apiPath, "models/:model/:id"),
77
+ handler: resolver.resolve("./runtime/server/api/models/[model]/[id].patch")
73
78
  });
74
79
  addServerHandler({
75
- route: joinURL(_options.apiPath, "/:model/:id"),
76
- handler: resolver.resolve("./runtime/server/api/[model]/[id].delete")
80
+ route: joinURL(_options.apiPath, "models/:model/:id"),
81
+ handler: resolver.resolve("./runtime/server/api/models/[model]/[id].delete")
77
82
  });
78
83
  addServerHandler({
79
- route: joinURL(_options.apiPath, "/:model/:id"),
80
- handler: resolver.resolve("./runtime/server/api/[model]/[id].get")
84
+ route: joinURL(_options.apiPath, "models/:model/:id"),
85
+ handler: resolver.resolve("./runtime/server/api/models/[model]/[id].get")
81
86
  });
87
+ addImportsDir(resolver.resolve("./runtime/composables"));
82
88
  }
83
89
  });
84
90
 
@@ -0,0 +1,19 @@
1
+ export type Status = 'idle' | 'pending' | 'success' | 'error';
2
+ export type FetchPolicy = 'cache-first' | 'fetch-only' | 'cache-only' | 'cache-and-fetch';
3
+ export type Error = {
4
+ error: boolean;
5
+ url: string;
6
+ statusCode: number;
7
+ statusMessage: string;
8
+ message: string;
9
+ stack?: string[];
10
+ };
11
+ export declare function getConfig(): {
12
+ apiPath: string;
13
+ fetchPolicy: FetchPolicy;
14
+ };
15
+ /**
16
+ * @param instance a custom fetch method created by `$fetch.create`
17
+ */
18
+ export declare function provideFetch(instance: typeof $fetch): void;
19
+ export declare function getFetch(): import("nitropack/types").$Fetch<unknown, import("nitropack/types").NitroFetchRequest>;
@@ -0,0 +1,12 @@
1
+ import { useRuntimeConfig } from "#imports";
2
+ export function getConfig() {
3
+ const config = useRuntimeConfig();
4
+ return config.public.zenstack;
5
+ }
6
+ let _fetch = $fetch;
7
+ export function provideFetch(instance) {
8
+ _fetch = instance;
9
+ }
10
+ export function getFetch() {
11
+ return _fetch;
12
+ }
@@ -0,0 +1,6 @@
1
+ export { useZenstackCreate } from './useZenstackCreate/index.js';
2
+ export { useZenstackReadMany } from './useZenstackReadMany/index.js';
3
+ export { useZenstackRead } from './useZenstackRead/index.js';
4
+ export { useZenstackDelete } from './useZenstackDelete/index.js';
5
+ export { useZenstackUpdate } from './useZenstackUpdate/index.js';
6
+ export { provideFetch as provideZenstackFetch } from './common.js';
@@ -0,0 +1,6 @@
1
+ export { useZenstackCreate } from "./useZenstackCreate/index.js";
2
+ export { useZenstackReadMany } from "./useZenstackReadMany/index.js";
3
+ export { useZenstackRead } from "./useZenstackRead/index.js";
4
+ export { useZenstackDelete } from "./useZenstackDelete/index.js";
5
+ export { useZenstackUpdate } from "./useZenstackUpdate/index.js";
6
+ export { provideFetch as provideZenstackFetch } from "./common.js";
@@ -0,0 +1,10 @@
1
+ import type { $Zmodel, $Zitem, $ZcreateData } from '#build/types/nuxt-zenstack';
2
+ import type { Status, Error } from '../common.js';
3
+ import { type Ref } from '#imports';
4
+ export declare function useZenstackCreate<Zmodel extends $Zmodel, Zitem extends $Zitem<Zmodel>>(model: Zmodel): {
5
+ data: Ref<Zitem | null>;
6
+ error: Ref<Error | null>;
7
+ status: Ref<Status>;
8
+ mutate: (input: $ZcreateData<Zmodel>) => Promise<void>;
9
+ reset: () => void;
10
+ };
@@ -0,0 +1,40 @@
1
+ import { getConfig, getFetch } from "../common.js";
2
+ import { ref } from "#imports";
3
+ import { joinURL } from "ufo";
4
+ import { useZenstackStore } from "../useZenstackStore/index.js";
5
+ export function useZenstackCreate(model) {
6
+ const data = ref(null);
7
+ const error = ref(null);
8
+ const status = ref("idle");
9
+ const store = useZenstackStore();
10
+ const config = getConfig();
11
+ function reset() {
12
+ data.value = null;
13
+ error.value = null;
14
+ status.value = "idle";
15
+ }
16
+ async function mutate(input) {
17
+ const _fetch = getFetch();
18
+ const url = joinURL(config.apiPath, `/models/${model.toString()}`);
19
+ const method = "POST";
20
+ const body = { data: input };
21
+ store.addToFetchHistory({
22
+ model: model.toString(),
23
+ method,
24
+ body: JSON.stringify(body)
25
+ });
26
+ reset();
27
+ status.value = "pending";
28
+ try {
29
+ const res = await _fetch(url, { method, body });
30
+ if (res.data)
31
+ store.setOne(model, res.data);
32
+ data.value = res.data;
33
+ status.value = "success";
34
+ } catch (err) {
35
+ error.value = err.data;
36
+ status.value = "error";
37
+ }
38
+ }
39
+ return { data, error, status, mutate, reset };
40
+ }
@@ -0,0 +1,10 @@
1
+ import type { $Zmodel, $Zitem, $Zid } from '#build/types/nuxt-zenstack';
2
+ import type { Status, Error } from '../common.js';
3
+ import { type Ref } from '#imports';
4
+ export declare function useZenstackDelete<Zmodel extends $Zmodel, Zitem extends $Zitem<Zmodel>>(model: Zmodel): {
5
+ data: Ref<Zitem | null>;
6
+ error: Ref<Error | null>;
7
+ status: Ref<Status>;
8
+ mutate: (id: $Zid<Zmodel>) => Promise<void>;
9
+ reset: () => void;
10
+ };
@@ -0,0 +1,38 @@
1
+ import { ref } from "#imports";
2
+ import { joinURL } from "ufo";
3
+ import { useZenstackStore } from "../useZenstackStore/index.js";
4
+ import { getConfig, getFetch } from "../common.js";
5
+ export function useZenstackDelete(model) {
6
+ const data = ref(null);
7
+ const error = ref(null);
8
+ const status = ref("idle");
9
+ const store = useZenstackStore();
10
+ const config = getConfig();
11
+ function reset() {
12
+ data.value = null;
13
+ error.value = null;
14
+ status.value = "idle";
15
+ }
16
+ async function mutate(id) {
17
+ const _fetch = getFetch();
18
+ const url = joinURL(config.apiPath, `/models/${model.toString()}/${id}`);
19
+ const method = "DELETE";
20
+ store.addToFetchHistory({
21
+ model: model.toString(),
22
+ id,
23
+ method
24
+ });
25
+ reset();
26
+ status.value = "pending";
27
+ try {
28
+ const res = await _fetch(url, { method });
29
+ store.deleteOne(model, id);
30
+ data.value = res.data;
31
+ status.value = "success";
32
+ } catch (err) {
33
+ error.value = err.data;
34
+ status.value = "error";
35
+ }
36
+ }
37
+ return { data, error, status, mutate, reset };
38
+ }
@@ -0,0 +1,30 @@
1
+ import type { $Zmodel, $Zitem, $Zid, $Zinclude } from '#build/types/nuxt-zenstack';
2
+ import type { Status, FetchPolicy } from '../common.js';
3
+ import type { Ref, MaybeRefOrGetter } from '#imports';
4
+ type Options<Zinclude> = {
5
+ /**
6
+ * - `cache-first` means that the query will first try to fetch the data from the store. If the data is not found in the store, it will fetch the data from the server and update the store.
7
+ * - `cache-and-fetch` means that the query will first try to fetch the data from the cache. It will then fetch the data from the server and update the cache.
8
+ * - `fetch-only` means that the query will only fetch the data from the server. The data will be stored in the srore when the query is resolved.
9
+ * - `cache-only` means that the query will only fetch the data from the store.
10
+ * @default 'cache-first'
11
+ */
12
+ fetchPolicy?: FetchPolicy;
13
+ /**
14
+ * Whether to fetch the data immediately (when the composable is called).
15
+ * @default true
16
+ */
17
+ immediate?: boolean;
18
+ /**
19
+ * The relations to include in the query.
20
+ * @default undefined
21
+ */
22
+ include?: MaybeRefOrGetter<Zinclude>;
23
+ };
24
+ export declare function useZenstackRead<Zmodel extends $Zmodel, Zinclude extends $Zinclude<Zmodel>, Zitem extends $Zitem<Zmodel, Zinclude>>(model: Zmodel, id: $Zid<Zmodel>, opts?: Options<Zinclude>): Promise<{
25
+ data: Ref<Zitem | null>;
26
+ error: Ref<Error | null>;
27
+ status: Ref<Status>;
28
+ refetch: () => Promise<void>;
29
+ }>;
30
+ export {};
@@ -0,0 +1,70 @@
1
+ import { ref, toValue, watch, isRef, useNuxtApp } from "#imports";
2
+ import { joinURL } from "ufo";
3
+ import { useZenstackStore } from "../useZenstackStore/index.js";
4
+ import { stringify } from "superjson";
5
+ import { getConfig, getFetch } from "../common.js";
6
+ export async function useZenstackRead(model, id, opts = {}) {
7
+ const data = ref(null);
8
+ const error = ref(null);
9
+ const status = ref("idle");
10
+ const store = useZenstackStore();
11
+ const config = getConfig();
12
+ const nuxtApp = useNuxtApp();
13
+ const watchedOptions = [opts.include].filter((option) => isRef(option) || typeof option === "function");
14
+ const method = "GET";
15
+ opts.fetchPolicy ??= config.fetchPolicy;
16
+ opts.immediate ??= true;
17
+ if (opts.fetchPolicy !== "fetch-only")
18
+ updateData();
19
+ watch(store.state, () => updateData());
20
+ watch(watchedOptions, () => refetch());
21
+ if (shouldFetchInitially())
22
+ await refetch();
23
+ function shouldFetchInitially() {
24
+ if (opts.immediate === false)
25
+ return false;
26
+ const entry = store.fetchHistory.value.find((entry2) => entry2.model === model && entry2.id === id && entry2.method === method && entry2.query === JSON.stringify(getQuery()));
27
+ if (entry?.ssr && nuxtApp.isHydrating)
28
+ return false;
29
+ if (opts.fetchPolicy === "cache-first" && entry)
30
+ return false;
31
+ return true;
32
+ }
33
+ function updateData() {
34
+ data.value = store.getOne(model, id);
35
+ }
36
+ function getQuery() {
37
+ const json = stringify({
38
+ include: toValue(opts.include)
39
+ });
40
+ return { q: json };
41
+ }
42
+ async function refetch() {
43
+ if (opts.fetchPolicy === "cache-only")
44
+ return;
45
+ const _fetch = getFetch();
46
+ const url = joinURL(config.apiPath, `/models/${model.toString()}/${id}`);
47
+ const query = getQuery();
48
+ store.addToFetchHistory({
49
+ model: model.toString(),
50
+ id,
51
+ method,
52
+ query: JSON.stringify(query)
53
+ });
54
+ error.value = null;
55
+ status.value = "pending";
56
+ try {
57
+ const res = await _fetch(url, { method, query });
58
+ if (res.data)
59
+ store.setOne(model, res.data);
60
+ else
61
+ store.deleteOne(model, id);
62
+ updateData();
63
+ status.value = "success";
64
+ } catch (err) {
65
+ error.value = err.data;
66
+ status.value = "error";
67
+ }
68
+ }
69
+ return { data, error, status, refetch };
70
+ }
@@ -0,0 +1,50 @@
1
+ import type { $Zmodel, $Zitem, $Zinclude, $Zwhere, $ZorderBy } from '#build/types/nuxt-zenstack';
2
+ import type { Status, FetchPolicy } from '../common.js';
3
+ import type { MaybeRefOrGetter, Ref } from '#imports';
4
+ type Options<Zinclude, Zwhere, ZorderBy> = {
5
+ /**
6
+ * - `cache-first` means that the query will first try to fetch the data from the store. If the data is not found in the store, it will fetch the data from the server and update the store.
7
+ * - `cache-and-fetch` means that the query will first try to fetch the data from the cache. It will then fetch the data from the server and update the cache.
8
+ * - `fetch-only` means that the query will only fetch the data from the server. The data will be stored in the srore when the query is resolved.
9
+ * - `cache-only` means that the query will only fetch the data from the store.
10
+ * @default 'cache-first'
11
+ */
12
+ fetchPolicy?: FetchPolicy;
13
+ /**
14
+ * Whether to fetch the data immediately (when the composable is called).
15
+ * @default true
16
+ */
17
+ immediate?: boolean;
18
+ /**
19
+ * The relations to include in the query.
20
+ * @default undefined
21
+ */
22
+ include?: MaybeRefOrGetter<Zinclude>;
23
+ /**
24
+ * The filter to apply to the query.
25
+ * @default undefined
26
+ */
27
+ where?: MaybeRefOrGetter<Zwhere>;
28
+ /**
29
+ * The order to apply to the query.
30
+ * @default undefined
31
+ */
32
+ orderBy?: MaybeRefOrGetter<ZorderBy>;
33
+ /**
34
+ * The number of items to skip.
35
+ * @default 0
36
+ */
37
+ skip?: MaybeRefOrGetter<number>;
38
+ /**
39
+ * The number of items to take.
40
+ * @default 1000
41
+ */
42
+ take?: MaybeRefOrGetter<number>;
43
+ };
44
+ export declare function useZenstackReadMany<Zmodel extends $Zmodel, Zinclude extends $Zinclude<Zmodel>, Zitem extends $Zitem<Zmodel, Zinclude>, Zwhere extends $Zwhere<Zmodel>, ZorderBy extends $ZorderBy<Zmodel>>(model: Zmodel, opts?: Options<Zinclude, Zwhere, ZorderBy>): Promise<{
45
+ data: Ref<Zitem[] | null>;
46
+ error: Ref<Error | null>;
47
+ status: Ref<Status>;
48
+ refetch: () => Promise<void>;
49
+ }>;
50
+ export {};
@@ -0,0 +1,75 @@
1
+ import { ref, toValue, watch, isRef, useNuxtApp } from "#imports";
2
+ import { joinURL } from "ufo";
3
+ import { useZenstackStore } from "../useZenstackStore/index.js";
4
+ import { stringify } from "superjson";
5
+ import { getConfig, getFetch } from "../common.js";
6
+ export async function useZenstackReadMany(model, opts = {}) {
7
+ const data = ref(null);
8
+ const error = ref(null);
9
+ const status = ref("idle");
10
+ const store = useZenstackStore();
11
+ const config = getConfig();
12
+ const nuxtApp = useNuxtApp();
13
+ const watchedOptions = [opts.include, opts.where, opts.orderBy, opts.skip, opts.take].filter((option) => isRef(option) || typeof option === "function");
14
+ const method = "GET";
15
+ opts.fetchPolicy ??= config.fetchPolicy;
16
+ opts.immediate ??= true;
17
+ opts.skip ??= 0;
18
+ opts.take ??= 1e3;
19
+ if (opts.fetchPolicy !== "fetch-only")
20
+ updateData();
21
+ watch(store.state, () => updateData());
22
+ watch(watchedOptions, () => {
23
+ updateData();
24
+ refetch();
25
+ });
26
+ if (shouldFetchInitially())
27
+ await refetch();
28
+ function shouldFetchInitially() {
29
+ if (opts.immediate === false)
30
+ return false;
31
+ const entry = store.fetchHistory.value.find((entry2) => entry2.model === model && entry2.method === method && entry2.query === JSON.stringify(getQuery()));
32
+ if (entry?.ssr && nuxtApp.isHydrating)
33
+ return false;
34
+ if (opts.fetchPolicy === "cache-first" && entry)
35
+ return false;
36
+ return true;
37
+ }
38
+ function updateData() {
39
+ data.value = store.getMany(model);
40
+ }
41
+ function getQuery() {
42
+ const json = stringify({
43
+ include: toValue(opts.include),
44
+ where: toValue(opts.where),
45
+ orderBy: toValue(opts.orderBy),
46
+ skip: toValue(opts.skip),
47
+ take: toValue(opts.take)
48
+ });
49
+ return { q: json };
50
+ }
51
+ async function refetch() {
52
+ if (opts.fetchPolicy === "cache-only")
53
+ return;
54
+ const _fetch = getFetch();
55
+ const url = joinURL(config.apiPath, `/models/${model.toString()}`);
56
+ const query = getQuery();
57
+ store.addToFetchHistory({
58
+ model: model.toString(),
59
+ method,
60
+ query: JSON.stringify(query)
61
+ });
62
+ error.value = null;
63
+ status.value = "pending";
64
+ try {
65
+ const res = await _fetch(url, { method, query });
66
+ store.setMany(model, res.data);
67
+ updateData();
68
+ status.value = "success";
69
+ } catch (err) {
70
+ error.value = err.data;
71
+ status.value = "error";
72
+ }
73
+ }
74
+ return { data, error, status, refetch };
75
+ }
@@ -0,0 +1,5 @@
1
+ import type { $Zmodel, $Zdef } from '#build/types/nuxt-zenstack';
2
+ export declare function getModelDef(model: $Zmodel): $Zdef;
3
+ export declare function isIdString(model: $Zmodel): boolean;
4
+ export declare function getModels(): $Zmodel[];
5
+ export declare function isValidModel(model: $Zmodel): boolean;
@@ -0,0 +1,16 @@
1
+ import { zenstackSchema } from "#build/nuxt-zenstack.mjs";
2
+ export function getModelDef(model) {
3
+ return zenstackSchema.models[model];
4
+ }
5
+ export function isIdString(model) {
6
+ const modelDef = getModelDef(model);
7
+ if (!modelDef.uniqueFields.id)
8
+ throw new Error(`Model ${model.toString()} does not have an id as a unique field`);
9
+ return modelDef.uniqueFields.id.type === "String";
10
+ }
11
+ export function getModels() {
12
+ return Object.keys(zenstackSchema.models);
13
+ }
14
+ export function isValidModel(model) {
15
+ return getModels().includes(model);
16
+ }
@@ -0,0 +1,46 @@
1
+ import type { $Zmodel, $Zid } from '#build/types/nuxt-zenstack';
2
+ type FetchEntry = {
3
+ model: string;
4
+ method: 'GET' | 'PATCH' | 'POST' | 'DELETE';
5
+ ssr: boolean;
6
+ timestamp: number;
7
+ id?: string | number;
8
+ query?: string;
9
+ body?: string;
10
+ };
11
+ export declare function useZenstackStore(): {
12
+ state: Readonly<import("vue").Ref<{
13
+ readonly [x: string]: {
14
+ readonly [x: string]: object;
15
+ };
16
+ }, {
17
+ readonly [x: string]: {
18
+ readonly [x: string]: object;
19
+ };
20
+ }>>;
21
+ fetchHistory: Readonly<import("vue").Ref<readonly {
22
+ readonly model: string;
23
+ readonly method: "GET" | "PATCH" | "POST" | "DELETE";
24
+ readonly ssr: boolean;
25
+ readonly timestamp: number;
26
+ readonly id?: string | number | undefined;
27
+ readonly query?: string | undefined;
28
+ readonly body?: string | undefined;
29
+ }[], readonly {
30
+ readonly model: string;
31
+ readonly method: "GET" | "PATCH" | "POST" | "DELETE";
32
+ readonly ssr: boolean;
33
+ readonly timestamp: number;
34
+ readonly id?: string | number | undefined;
35
+ readonly query?: string | undefined;
36
+ readonly body?: string | undefined;
37
+ }[]>>;
38
+ setOne: (model: $Zmodel, input: object) => void;
39
+ setMany: (model: $Zmodel, input: object[]) => void;
40
+ getOne: <Zmodel extends $Zmodel>(model: Zmodel, input: $Zid<Zmodel>) => any;
41
+ getMany: (model: $Zmodel) => any;
42
+ deleteOne: <Model extends $Zmodel>(model: Model, id: $Zid<Model>) => void;
43
+ deleteMany: (model: $Zmodel) => void;
44
+ addToFetchHistory: (entry: Omit<FetchEntry, "ssr" | "timestamp">) => void;
45
+ };
46
+ export {};
@@ -0,0 +1,71 @@
1
+ import { generateNormalizrSchema, getNormalizrSchema } from "./normalization.js";
2
+ import { normalize, denormalize } from "normalizr";
3
+ import { defu } from "defu";
4
+ import { readonly, useState } from "#imports";
5
+ generateNormalizrSchema();
6
+ export function useZenstackStore() {
7
+ const state = useState("zenstack-state", () => ({}));
8
+ const fetchHistory = useState("zenstack-fetch-history", () => []);
9
+ function setOne(model, input) {
10
+ const schema = getNormalizrSchema(model);
11
+ const res = normalize(input, schema);
12
+ state.value = defu(res.entities, state.value);
13
+ }
14
+ function setMany(model, input) {
15
+ const schema = [getNormalizrSchema(model)];
16
+ const res = normalize(input, schema);
17
+ state.value = defu(res.entities, state.value);
18
+ }
19
+ function getOne(model, input) {
20
+ const schema = getNormalizrSchema(model);
21
+ return denormalize(input, schema, state.value) ?? null;
22
+ }
23
+ function getMany(model) {
24
+ const input = Object.keys(state.value[model.toString()] ?? {});
25
+ const schema = [getNormalizrSchema(model)];
26
+ return denormalize(input, schema, state.value) ?? [];
27
+ }
28
+ function deleteOne(model, id) {
29
+ const newState = {};
30
+ for (const _model in state.value) {
31
+ for (const _id in state.value[_model]) {
32
+ if (_id == id && _model === model)
33
+ continue;
34
+ newState[_model] ||= {};
35
+ newState[_model][_id] = { ...state.value[_model][_id] };
36
+ }
37
+ }
38
+ state.value = newState;
39
+ }
40
+ function deleteMany(model) {
41
+ const newState = {};
42
+ for (const _model in state.value) {
43
+ if (_model === model)
44
+ continue;
45
+ newState[_model] = { ...state.value[_model] };
46
+ }
47
+ state.value = newState;
48
+ }
49
+ function addToFetchHistory(entry) {
50
+ fetchHistory.value.push({
51
+ method: entry.method,
52
+ model: entry.model,
53
+ body: entry.body,
54
+ id: entry.id,
55
+ query: entry.query,
56
+ ssr: typeof window === "undefined",
57
+ timestamp: (/* @__PURE__ */ new Date()).getTime()
58
+ });
59
+ }
60
+ return {
61
+ state: readonly(state),
62
+ fetchHistory: readonly(fetchHistory),
63
+ setOne,
64
+ setMany,
65
+ getOne,
66
+ getMany,
67
+ deleteOne,
68
+ deleteMany,
69
+ addToFetchHistory
70
+ };
71
+ }
@@ -0,0 +1,5 @@
1
+ import { schema as normalizrSchema } from 'normalizr';
2
+ import type { $Zmodel } from '#build/types/nuxt-zenstack';
3
+ declare function getNormalizrSchema(model: $Zmodel): normalizrSchema.Entity<any>;
4
+ declare function generateNormalizrSchema(): void;
5
+ export { getNormalizrSchema, generateNormalizrSchema };
@@ -0,0 +1,31 @@
1
+ import { schema as normalizrSchema } from "normalizr";
2
+ import { getModelDef, getModels, isValidModel } from "./helpers.js";
3
+ const NORMALIZR_SCHEMA = /* @__PURE__ */ new Map();
4
+ function getNormalizrSchema(model) {
5
+ const schema = NORMALIZR_SCHEMA.get(model);
6
+ if (!schema)
7
+ throw new Error(`Model ${model.toString()} not defined`);
8
+ return schema;
9
+ }
10
+ function generateNormalizrSchema() {
11
+ const models = getModels();
12
+ for (const model of models) {
13
+ NORMALIZR_SCHEMA.set(model, new normalizrSchema.Entity(model.toString()));
14
+ }
15
+ for (const model of models) {
16
+ const entity = getNormalizrSchema(model);
17
+ const modelDef = getModelDef(model);
18
+ const definition = {};
19
+ for (const field of Object.values(modelDef.fields)) {
20
+ if (isValidModel(field.type)) {
21
+ const relatedModel = field.type;
22
+ const relatedEntity = getNormalizrSchema(relatedModel);
23
+ definition[field.name] = field.array ? [relatedEntity] : relatedEntity;
24
+ }
25
+ }
26
+ if (Object.keys(definition).length > 0) {
27
+ entity.define(definition);
28
+ }
29
+ }
30
+ }
31
+ export { getNormalizrSchema, generateNormalizrSchema };
@@ -0,0 +1,10 @@
1
+ import type { $Zmodel, $Zitem, $ZupdateData, $Zid } from '#build/types/nuxt-zenstack';
2
+ import type { Status } from '../common.js';
3
+ import type { Ref } from '#imports';
4
+ export declare function useZenstackUpdate<Zmodel extends $Zmodel, Zitem extends $Zitem<Zmodel>>(model: Zmodel): {
5
+ data: Ref<Zitem | null>;
6
+ error: Ref<Error | null>;
7
+ status: Ref<Status>;
8
+ mutate: (id: $Zid<Zmodel>, input: $ZupdateData<Zmodel>) => Promise<void>;
9
+ reset: () => void;
10
+ };
@@ -0,0 +1,40 @@
1
+ import { ref } from "#imports";
2
+ import { joinURL } from "ufo";
3
+ import { useZenstackStore } from "../useZenstackStore/index.js";
4
+ import { getConfig, getFetch } from "../common.js";
5
+ export function useZenstackUpdate(model) {
6
+ const data = ref(null);
7
+ const error = ref(null);
8
+ const status = ref("idle");
9
+ const store = useZenstackStore();
10
+ const config = getConfig();
11
+ function reset() {
12
+ data.value = null;
13
+ error.value = null;
14
+ status.value = "idle";
15
+ }
16
+ async function mutate(id, input) {
17
+ const _fetch = getFetch();
18
+ const url = joinURL(config.apiPath, `/models/${model.toString()}/${id}`);
19
+ const body = { data: input };
20
+ const method = "PATCH";
21
+ store.addToFetchHistory({
22
+ model: model.toString(),
23
+ method,
24
+ body: JSON.stringify(body)
25
+ });
26
+ reset();
27
+ status.value = "pending";
28
+ try {
29
+ const res = await _fetch(url, { method, body });
30
+ if (res.data)
31
+ store.setOne(model, res.data);
32
+ data.value = res.data;
33
+ status.value = "success";
34
+ } catch (err) {
35
+ error.value = err.data;
36
+ status.value = "error";
37
+ }
38
+ }
39
+ return { data, error, status, mutate, reset };
40
+ }
@@ -1,6 +1,6 @@
1
1
  import { defineEventHandler } from "h3";
2
- import { parseModel, parseId } from "../../utils/parsers.js";
3
- import { getModelOperations } from "../../utils/index.js";
2
+ import { parseModel, parseId } from "../../../utils/parsers.js";
3
+ import { getModelOperations } from "../../../utils/index.js";
4
4
  export default defineEventHandler(async (event) => {
5
5
  const model = parseModel(event);
6
6
  const id = parseId(event, model);
@@ -1,6 +1,6 @@
1
- import { parseModel, parseId, parseReadArgs } from "../../utils/parsers.js";
1
+ import { parseModel, parseId, parseReadArgs } from "../../../utils/parsers.js";
2
2
  import { defineEventHandler } from "h3";
3
- import { getModelOperations } from "../../utils/index.js";
3
+ import { getModelOperations } from "../../../utils/index.js";
4
4
  export default defineEventHandler(async (event) => {
5
5
  const model = parseModel(event);
6
6
  const id = parseId(event, model);
@@ -1,13 +1,15 @@
1
- import { parseModel, parseId, parseUpdateData } from "../../utils/parsers.js";
1
+ import { parseModel, parseId, parseUpdateData } from "../../../utils/parsers.js";
2
2
  import { defineEventHandler } from "h3";
3
- import { getModelOperations } from "../../utils/index.js";
3
+ import { getModelOperations, includeAll } from "../../../utils/index.js";
4
4
  export default defineEventHandler(async (event) => {
5
5
  const model = parseModel(event);
6
6
  const id = parseId(event, model);
7
7
  const updateData = await parseUpdateData(event, model);
8
8
  const operations = getModelOperations(model);
9
+ const include = includeAll(model);
9
10
  const data = await operations.update({
10
11
  data: updateData.data,
12
+ include,
11
13
  where: {
12
14
  id
13
15
  }
@@ -1,6 +1,6 @@
1
- import { parseModel, parseReadArgs } from "../../utils/parsers.js";
1
+ import { parseModel, parseReadArgs } from "../../../utils/parsers.js";
2
2
  import { defineEventHandler } from "h3";
3
- import { getModelOperations } from "../../utils/index.js";
3
+ import { getModelOperations } from "../../../utils/index.js";
4
4
  export default defineEventHandler(async (event) => {
5
5
  const model = parseModel(event);
6
6
  const readArgs = parseReadArgs(event, model);
@@ -1,12 +1,14 @@
1
- import { parseModel, parseCreateData } from "../../utils/parsers.js";
1
+ import { parseModel, parseCreateData } from "../../../utils/parsers.js";
2
2
  import { defineEventHandler } from "h3";
3
- import { getModelOperations } from "../../utils/index.js";
3
+ import { getModelOperations, includeAll } from "../../../utils/index.js";
4
4
  export default defineEventHandler(async (event) => {
5
5
  const model = parseModel(event);
6
6
  const createData = await parseCreateData(event, model);
7
7
  const operations = getModelOperations(model);
8
+ const include = includeAll(model);
8
9
  const data = await operations.create({
9
- data: createData.data
10
+ data: createData.data,
11
+ include
10
12
  });
11
13
  return { data };
12
14
  });
@@ -1,11 +1,12 @@
1
1
  import type { ClientContract } from '@zenstackhq/orm';
2
- import type { $Zschema, $Zmodel, $Zoperations } from '#build/types/nuxt-zenstack';
2
+ import type { $Zschema, $Zmodel, $Zoperations, $Zdef } from '#build/types/nuxt-zenstack';
3
3
  declare let _client: ClientContract<$Zschema> | null;
4
4
  export declare function provideClient(client: typeof _client): void;
5
5
  export declare function getClient(): ClientContract<SchemaType>;
6
- export declare function getModelDef(model: $Zmodel): any;
6
+ export declare function getModelOperations<Zmodel extends $Zmodel>(model: Zmodel): $Zoperations<Zmodel>;
7
+ export declare function getModelDef(model: $Zmodel): $Zdef;
7
8
  export declare function isIdString(model: $Zmodel): boolean;
8
9
  export declare function getModels(): $Zmodel[];
9
10
  export declare function isValidModel(model: $Zmodel): boolean;
10
- export declare function getModelOperations<Zmodel extends $Zmodel>(model: Zmodel): $Zoperations<Zmodel>;
11
+ export declare function includeAll<Zmodel extends $Zmodel>(model: Zmodel): boolean | import("@zenstackhq/orm").SelectIncludeOmit<SchemaType, any, boolean, true>;
11
12
  export {};
@@ -9,11 +9,18 @@ export function getClient() {
9
9
  }
10
10
  throw new Error("ZenStack client not provided");
11
11
  }
12
+ export function getModelOperations(model) {
13
+ const db = getClient();
14
+ return db[model.toString().toLocaleLowerCase()];
15
+ }
12
16
  export function getModelDef(model) {
13
17
  return zenstackSchema.models[model];
14
18
  }
15
19
  export function isIdString(model) {
16
- return getModelDef(model).uniqueFields.id.type === "String";
20
+ const modelDef = getModelDef(model);
21
+ if (!modelDef.uniqueFields.id)
22
+ throw new Error(`Model ${model.toString()} does not have an id as a unique field`);
23
+ return modelDef.uniqueFields.id.type === "String";
17
24
  }
18
25
  export function getModels() {
19
26
  return Object.keys(zenstackSchema.models);
@@ -21,7 +28,21 @@ export function getModels() {
21
28
  export function isValidModel(model) {
22
29
  return getModels().includes(model);
23
30
  }
24
- export function getModelOperations(model) {
25
- const db = getClient();
26
- return db[model.toString().toLocaleLowerCase()];
31
+ export function includeAll(model) {
32
+ const include = {};
33
+ const modelSet = /* @__PURE__ */ new Set();
34
+ function iterate(_model, acc) {
35
+ const modelDef = getModelDef(_model);
36
+ for (const [field, fieldDef] of Object.entries(modelDef.fields)) {
37
+ if (isValidModel(fieldDef.type) && !modelSet.has(fieldDef.type)) {
38
+ acc["include"] ||= {};
39
+ acc["include"][field] = {};
40
+ modelSet.add(fieldDef.type);
41
+ iterate(fieldDef.type, acc["include"][field]);
42
+ }
43
+ }
44
+ }
45
+ modelSet.add(model);
46
+ iterate(model, include);
47
+ return include["include"] ?? {};
27
48
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bg-dev/nuxt-zenstack",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "ZenStack integration for Nuxt",
5
5
  "repository": {
6
6
  "url": "https://github.com/becem-gharbi/nuxt-zenstack"
@@ -36,6 +36,7 @@
36
36
  "prepack": "nuxt-module-build build",
37
37
  "dev": "npm run dev:prepare && nuxt dev playground",
38
38
  "dev:build": "nuxt build playground",
39
+ "dev:preview": "nuxt preview playground",
39
40
  "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
40
41
  "dev:db:generate": "cd playground && zen generate",
41
42
  "dev:db:push": "cd playground && zen db push",
@@ -52,6 +53,7 @@
52
53
  "@nuxt/kit": "^4.2.2",
53
54
  "@zenstackhq/orm": "3.2.1",
54
55
  "defu": "^6.1.4",
56
+ "normalizr": "^3.6.2",
55
57
  "superjson": "^2.2.6",
56
58
  "ufo": "^1.6.3"
57
59
  },
@@ -62,12 +64,14 @@
62
64
  "@nuxt/module-builder": "^1.0.2",
63
65
  "@nuxt/schema": "^4.2.2",
64
66
  "@nuxt/test-utils": "^3.23.0",
67
+ "@nuxt/ui": "^4.3.0",
65
68
  "@types/node": "latest",
66
69
  "@zenstackhq/cli": "3.2.1",
67
70
  "changelogen": "^0.6.2",
68
71
  "eslint": "^9.39.2",
69
72
  "libsql": "^0.5.22",
70
73
  "nuxt": "^4.2.2",
74
+ "playwright-core": "^1.57.0",
71
75
  "typescript": "~5.9.3",
72
76
  "vitest": "^4.0.17",
73
77
  "vue-tsc": "^3.2.2"