@bg-dev/nuxt-zenstack 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/module.d.mts +2 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +61 -12
  4. package/dist/runtime/composables/common.d.ts +3 -13
  5. package/dist/runtime/composables/useZenstackCreate/index.d.ts +3 -3
  6. package/dist/runtime/composables/useZenstackDelete/index.d.ts +3 -3
  7. package/dist/runtime/composables/useZenstackRead/index.d.ts +4 -4
  8. package/dist/runtime/composables/useZenstackRead/index.js +8 -7
  9. package/dist/runtime/composables/useZenstackReadMany/index.d.ts +4 -3
  10. package/dist/runtime/composables/useZenstackReadMany/index.js +21 -8
  11. package/dist/runtime/composables/useZenstackReadMany/orderBy.d.ts +5 -0
  12. package/dist/runtime/composables/useZenstackReadMany/orderBy.js +25 -0
  13. package/dist/runtime/composables/useZenstackReadMany/where.d.ts +2 -0
  14. package/dist/runtime/composables/useZenstackReadMany/where.js +235 -0
  15. package/dist/runtime/composables/useZenstackRealtime/index.d.ts +6 -0
  16. package/dist/runtime/composables/useZenstackRealtime/index.js +53 -0
  17. package/dist/runtime/composables/useZenstackStore/index.d.ts +6 -2
  18. package/dist/runtime/composables/useZenstackStore/index.js +3 -4
  19. package/dist/runtime/composables/useZenstackStore/normalization.d.ts +16 -3
  20. package/dist/runtime/composables/useZenstackStore/normalization.js +16 -3
  21. package/dist/runtime/composables/useZenstackUpdate/index.d.ts +2 -2
  22. package/dist/runtime/server/api/models/[model]/[id].delete.d.ts +1 -1
  23. package/dist/runtime/server/api/models/[model]/[id].delete.js +5 -9
  24. package/dist/runtime/server/api/models/[model]/[id].get.d.ts +1 -1
  25. package/dist/runtime/server/api/models/[model]/[id].get.js +5 -10
  26. package/dist/runtime/server/api/models/[model]/[id].patch.d.ts +1 -1
  27. package/dist/runtime/server/api/models/[model]/[id].patch.js +6 -13
  28. package/dist/runtime/server/api/models/[model]/index.get.d.ts +2 -2
  29. package/dist/runtime/server/api/models/[model]/index.get.js +9 -7
  30. package/dist/runtime/server/api/models/[model]/index.post.d.ts +1 -1
  31. package/dist/runtime/server/api/models/[model]/index.post.js +6 -10
  32. package/dist/runtime/server/routes/realtime.d.ts +2 -0
  33. package/dist/runtime/server/routes/realtime.js +18 -0
  34. package/dist/runtime/server/utils/helpers.d.ts +7 -0
  35. package/dist/runtime/server/utils/helpers.js +34 -0
  36. package/dist/runtime/server/utils/index.d.ts +15 -12
  37. package/dist/runtime/server/utils/index.js +12 -46
  38. package/dist/runtime/server/utils/operations/common.d.ts +10 -0
  39. package/dist/runtime/server/utils/operations/common.js +52 -0
  40. package/dist/runtime/server/utils/operations/create.d.ts +10 -0
  41. package/dist/runtime/server/utils/operations/create.js +15 -0
  42. package/dist/runtime/server/utils/operations/delete.d.ts +10 -0
  43. package/dist/runtime/server/utils/operations/delete.js +16 -0
  44. package/dist/runtime/server/utils/operations/findMany.d.ts +14 -0
  45. package/dist/runtime/server/utils/operations/findMany.js +14 -0
  46. package/dist/runtime/server/utils/operations/findUnique.d.ts +11 -0
  47. package/dist/runtime/server/utils/operations/findUnique.js +14 -0
  48. package/dist/runtime/server/utils/operations/update.d.ts +11 -0
  49. package/dist/runtime/server/utils/operations/update.js +19 -0
  50. package/dist/runtime/server/utils/parsers.d.ts +3 -2
  51. package/dist/runtime/server/utils/parsers.js +9 -3
  52. package/package.json +3 -1
package/dist/module.d.mts CHANGED
@@ -4,6 +4,8 @@ import { FetchPolicy } from '../dist/runtime/composables/common.js';
4
4
  interface ModuleOptions {
5
5
  apiPath: string;
6
6
  fetchPolicy: FetchPolicy;
7
+ realtime: boolean;
8
+ baseUrl: string;
7
9
  }
8
10
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
9
11
 
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bg-dev/nuxt-zenstack",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
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, addImportsDir } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, addTemplate, addServerTemplate, addTypeTemplate, addServerHandler, addImportsDir, addServerImports, addImports } 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.3";
6
+ const version = "0.0.5";
7
7
 
8
8
  const module$1 = defineNuxtModule({
9
9
  meta: {
@@ -14,21 +14,20 @@ const module$1 = defineNuxtModule({
14
14
  // Default configuration options of the Nuxt module
15
15
  defaults: {
16
16
  apiPath: "/api/_zenstack",
17
- fetchPolicy: "cache-first"
17
+ fetchPolicy: "cache-first",
18
+ realtime: false,
19
+ baseUrl: "http://localhost:3000"
18
20
  },
19
21
  setup(_options, _nuxt) {
20
22
  _nuxt.options.runtimeConfig.public = defu(_nuxt.options.runtimeConfig.public, {
21
23
  zenstack: {
22
24
  apiPath: _options.apiPath,
23
- fetchPolicy: _options.fetchPolicy
25
+ fetchPolicy: _options.fetchPolicy,
26
+ realtime: _options.realtime,
27
+ baseUrl: _options.baseUrl
24
28
  }
25
29
  });
26
30
  const resolver = createResolver(import.meta.url);
27
- addServerImports({
28
- from: resolver.resolve("./runtime/server/utils"),
29
- name: "provideClient",
30
- as: "provideZenstackClient"
31
- });
32
31
  addTemplate({
33
32
  write: true,
34
33
  filename: "nuxt-zenstack.mjs",
@@ -45,13 +44,16 @@ export { schema as zenstackSchema } from '~~/zenstack/schema'
45
44
  addTypeTemplate({
46
45
  filename: "types/nuxt-zenstack.d.ts",
47
46
  getContents: () => `
48
- import type { ModelOperations, ModelResult, IncludeInput, WhereInput, FindManyArgs, SimplifiedPlainResult, SelectIncludeOmit, QueryOptions, CreateArgs, UpdateArgs } from '@zenstackhq/orm'
47
+ import type { ClientContract, ModelOperations, ModelResult, IncludeInput, WhereInput, FindManyArgs, SimplifiedPlainResult, SelectIncludeOmit, QueryOptions, CreateArgs, UpdateArgs } from '@zenstackhq/orm'
49
48
  import type { SchemaType } from '~~/zenstack/schema'
50
49
  import type { ModelDef } from '@zenstackhq/orm/schema'
50
+ import type { H3Error } from 'h3'
51
+ import { ORMError, RejectedByPolicyReason, ORMErrorReason } from '@zenstackhq/orm'
51
52
 
52
53
  type ItemGetPayload<Zmodel extends $Zmodel, Args extends SelectIncludeOmit<SchemaType, Zmodel, true>, Options extends QueryOptions<SchemaType> = QueryOptions<SchemaType>> = SimplifiedPlainResult<SchemaType, Zmodel, Args, Options>
53
54
 
54
55
  export type $Zschema = SchemaType
56
+ export type $Zclient = ClientContract<SchemaType>
55
57
  export type $Zmodel = keyof SchemaType['models']
56
58
  export type $Zdef = ModelDef
57
59
  export type $Zoperations<Zmodel extends $Zmodel> = ModelOperations<SchemaType, Zmodel>
@@ -60,8 +62,41 @@ export type $Zinclude<Zmodel extends $Zmodel> = IncludeInput<SchemaType, Zmodel>
60
62
  export type $Zitem<Zmodel extends $Zmodel, Zinclude extends $Zinclude = undefined> = ItemGetPayload<Zmodel, { include: Zinclude }>
61
63
  export type $Zwhere<Zmodel extends $Zmodel> = WhereInput<SchemaType, Zmodel> | undefined
62
64
  export type $ZorderBy<Zmodel extends $Zmodel> = FindManyArgs<SchemaType, Zmodel>['orderBy'] | undefined
63
- export type $ZcreateData<Zmodel extends $Zmodel> = CreateArgs<SchemaType, Zmodel>['data']
64
- export type $ZupdateData<Zmodel extends $Zmodel> = UpdateArgs<SchemaType, Zmodel>['data']
65
+
66
+ type NonRelationalFields<Zmodel extends $Zmodel> = keyof $Zitem<Zmodel>
67
+ type CreateData<Zmodel extends $Zmodel> = CreateArgs<SchemaType, Zmodel>['data']
68
+ type UpdateData<Zmodel extends $Zmodel> = UpdateArgs<SchemaType, Zmodel>['data']
69
+
70
+ export type $ZcreateData<Zmodel extends $Zmodel> = Pick<CreateData<Zmodel>, NonRelationalFields<Zmodel>>
71
+ export type $ZupdateData<Zmodel extends $Zmodel> = Pick<UpdateData<Zmodel>, NonRelationalFields<Zmodel>>
72
+ export type $ZormError = ORMError
73
+ export type $Zerror = H3Error<{
74
+ ormErrorReason: ORMErrorReason
75
+ dbErrorCode?: unknown
76
+ dbErrorMessage?: string
77
+ model?: string
78
+ rejectedByPolicyReason?: RejectedByPolicyReason
79
+ }>
80
+
81
+ declare module 'h3' {
82
+ interface H3EventContext {
83
+ zenstack: {
84
+ client: ClientContract<SchemaType>
85
+ }
86
+ }
87
+ }
88
+
89
+ type RealtimeData = {
90
+ action: 'create' | 'update' | 'delete'
91
+ model: $Zmodel
92
+ ids: Array<number | string>
93
+ }
94
+
95
+ declare module 'nitropack' {
96
+ interface NitroRuntimeHooks {
97
+ 'zenstack:after_mutation': (action: 'create' | 'update' | 'delete', model: $Zmodel, items: Record<string, unknown>[]) => Promise<void> | void
98
+ }
99
+ }
65
100
  `
66
101
  });
67
102
  addServerHandler({
@@ -85,6 +120,20 @@ export type $ZupdateData<Zmodel extends $Zmodel> = UpdateArgs<SchemaType, Zmodel
85
120
  handler: resolver.resolve("./runtime/server/api/models/[model]/[id].get")
86
121
  });
87
122
  addImportsDir(resolver.resolve("./runtime/composables"));
123
+ addServerImports({
124
+ from: resolver.resolve("./runtime/server/utils"),
125
+ name: "useZenstack"
126
+ });
127
+ if (_options.realtime) {
128
+ addServerHandler({
129
+ route: joinURL(_options.apiPath, "realtime"),
130
+ handler: resolver.resolve("./runtime/server/routes/realtime")
131
+ });
132
+ addImports({
133
+ from: resolver.resolve("./runtime/composables/useZenstackRealtime"),
134
+ name: "useZenstackRealtime"
135
+ });
136
+ }
88
137
  }
89
138
  });
90
139
 
@@ -1,19 +1,9 @@
1
+ import type { ModuleOptions } from '../../module.js';
1
2
  export type Status = 'idle' | 'pending' | 'success' | 'error';
2
3
  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
- };
4
+ export declare function getConfig(): ModuleOptions;
15
5
  /**
16
6
  * @param instance a custom fetch method created by `$fetch.create`
17
7
  */
18
8
  export declare function provideFetch(instance: typeof $fetch): void;
19
- export declare function getFetch(): import("nitropack/types").$Fetch<unknown, import("nitropack/types").NitroFetchRequest>;
9
+ export declare function getFetch(): import("nitropack").$Fetch<unknown, import("nitropack").NitroFetchRequest>;
@@ -1,9 +1,9 @@
1
- import type { $Zmodel, $Zitem, $ZcreateData } from '#build/types/nuxt-zenstack';
2
- import type { Status, Error } from '../common.js';
1
+ import type { $Zmodel, $Zitem, $ZcreateData, $Zerror } from '#build/types/nuxt-zenstack';
2
+ import type { Status } from '../common.js';
3
3
  import { type Ref } from '#imports';
4
4
  export declare function useZenstackCreate<Zmodel extends $Zmodel, Zitem extends $Zitem<Zmodel>>(model: Zmodel): {
5
5
  data: Ref<Zitem | null>;
6
- error: Ref<Error | null>;
6
+ error: Ref<$Zerror | null>;
7
7
  status: Ref<Status>;
8
8
  mutate: (input: $ZcreateData<Zmodel>) => Promise<void>;
9
9
  reset: () => void;
@@ -1,9 +1,9 @@
1
- import type { $Zmodel, $Zitem, $Zid } from '#build/types/nuxt-zenstack';
2
- import type { Status, Error } from '../common.js';
1
+ import type { $Zmodel, $Zitem, $Zid, $Zerror } from '#build/types/nuxt-zenstack';
2
+ import type { Status } from '../common.js';
3
3
  import { type Ref } from '#imports';
4
4
  export declare function useZenstackDelete<Zmodel extends $Zmodel, Zitem extends $Zitem<Zmodel>>(model: Zmodel): {
5
5
  data: Ref<Zitem | null>;
6
- error: Ref<Error | null>;
6
+ error: Ref<$Zerror | null>;
7
7
  status: Ref<Status>;
8
8
  mutate: (id: $Zid<Zmodel>) => Promise<void>;
9
9
  reset: () => void;
@@ -1,6 +1,6 @@
1
- import type { $Zmodel, $Zitem, $Zid, $Zinclude } from '#build/types/nuxt-zenstack';
1
+ import type { $Zmodel, $Zitem, $Zid, $Zinclude, $Zerror } from '#build/types/nuxt-zenstack';
2
2
  import type { Status, FetchPolicy } from '../common.js';
3
- import type { Ref, MaybeRefOrGetter } from '#imports';
3
+ import type { Ref } from '#imports';
4
4
  type Options<Zinclude> = {
5
5
  /**
6
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.
@@ -19,11 +19,11 @@ type Options<Zinclude> = {
19
19
  * The relations to include in the query.
20
20
  * @default undefined
21
21
  */
22
- include?: MaybeRefOrGetter<Zinclude>;
22
+ include?: Zinclude;
23
23
  };
24
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
25
  data: Ref<Zitem | null>;
26
- error: Ref<Error | null>;
26
+ error: Ref<$Zerror | null>;
27
27
  status: Ref<Status>;
28
28
  refetch: () => Promise<void>;
29
29
  }>;
@@ -1,4 +1,4 @@
1
- import { ref, toValue, watch, isRef, useNuxtApp } from "#imports";
1
+ import { ref, watch, useNuxtApp } from "#imports";
2
2
  import { joinURL } from "ufo";
3
3
  import { useZenstackStore } from "../useZenstackStore/index.js";
4
4
  import { stringify } from "superjson";
@@ -10,19 +10,15 @@ export async function useZenstackRead(model, id, opts = {}) {
10
10
  const store = useZenstackStore();
11
11
  const config = getConfig();
12
12
  const nuxtApp = useNuxtApp();
13
- const watchedOptions = [opts.include].filter((option) => isRef(option) || typeof option === "function");
14
13
  const method = "GET";
15
14
  opts.fetchPolicy ??= config.fetchPolicy;
16
15
  opts.immediate ??= true;
17
16
  if (opts.fetchPolicy !== "fetch-only")
18
17
  updateData();
19
18
  watch(store.state, () => updateData());
20
- watch(watchedOptions, () => refetch());
21
19
  if (shouldFetchInitially())
22
20
  await refetch();
23
- function shouldFetchInitially() {
24
- if (opts.immediate === false)
25
- return false;
21
+ function canFetchAutomatically() {
26
22
  const entry = store.fetchHistory.value.find((entry2) => entry2.model === model && entry2.id === id && entry2.method === method && entry2.query === JSON.stringify(getQuery()));
27
23
  if (entry?.ssr && nuxtApp.isHydrating)
28
24
  return false;
@@ -30,12 +26,17 @@ export async function useZenstackRead(model, id, opts = {}) {
30
26
  return false;
31
27
  return true;
32
28
  }
29
+ function shouldFetchInitially() {
30
+ if (opts.immediate === false)
31
+ return false;
32
+ return canFetchAutomatically();
33
+ }
33
34
  function updateData() {
34
35
  data.value = store.getOne(model, id);
35
36
  }
36
37
  function getQuery() {
37
38
  const json = stringify({
38
- include: toValue(opts.include)
39
+ include: opts.include
39
40
  });
40
41
  return { q: json };
41
42
  }
@@ -1,4 +1,4 @@
1
- import type { $Zmodel, $Zitem, $Zinclude, $Zwhere, $ZorderBy } from '#build/types/nuxt-zenstack';
1
+ import type { $Zmodel, $Zitem, $Zinclude, $Zwhere, $ZorderBy, $Zerror } from '#build/types/nuxt-zenstack';
2
2
  import type { Status, FetchPolicy } from '../common.js';
3
3
  import type { MaybeRefOrGetter, Ref } from '#imports';
4
4
  type Options<Zinclude, Zwhere, ZorderBy> = {
@@ -19,7 +19,7 @@ type Options<Zinclude, Zwhere, ZorderBy> = {
19
19
  * The relations to include in the query.
20
20
  * @default undefined
21
21
  */
22
- include?: MaybeRefOrGetter<Zinclude>;
22
+ include?: Zinclude;
23
23
  /**
24
24
  * The filter to apply to the query.
25
25
  * @default undefined
@@ -43,8 +43,9 @@ type Options<Zinclude, Zwhere, ZorderBy> = {
43
43
  };
44
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
45
  data: Ref<Zitem[] | null>;
46
- error: Ref<Error | null>;
46
+ error: Ref<$Zerror | null>;
47
47
  status: Ref<Status>;
48
+ canFetchMore: Ref<boolean>;
48
49
  refetch: () => Promise<void>;
49
50
  }>;
50
51
  export {};
@@ -3,14 +3,17 @@ import { joinURL } from "ufo";
3
3
  import { useZenstackStore } from "../useZenstackStore/index.js";
4
4
  import { stringify } from "superjson";
5
5
  import { getConfig, getFetch } from "../common.js";
6
+ import { sort } from "./orderBy.js";
7
+ import { matchesWhere } from "./where.js";
6
8
  export async function useZenstackReadMany(model, opts = {}) {
7
9
  const data = ref(null);
8
10
  const error = ref(null);
9
11
  const status = ref("idle");
12
+ const canFetchMore = ref(true);
10
13
  const store = useZenstackStore();
11
14
  const config = getConfig();
12
15
  const nuxtApp = useNuxtApp();
13
- const watchedOptions = [opts.include, opts.where, opts.orderBy, opts.skip, opts.take].filter((option) => isRef(option) || typeof option === "function");
16
+ const watchedOptions = [opts.where, opts.orderBy, opts.skip, opts.take].filter((option) => isRef(option) || typeof option === "function");
14
17
  const method = "GET";
15
18
  opts.fetchPolicy ??= config.fetchPolicy;
16
19
  opts.immediate ??= true;
@@ -21,13 +24,12 @@ export async function useZenstackReadMany(model, opts = {}) {
21
24
  watch(store.state, () => updateData());
22
25
  watch(watchedOptions, () => {
23
26
  updateData();
24
- refetch();
27
+ if (canFetchAutomatically())
28
+ refetch();
25
29
  });
26
30
  if (shouldFetchInitially())
27
31
  await refetch();
28
- function shouldFetchInitially() {
29
- if (opts.immediate === false)
30
- return false;
32
+ function canFetchAutomatically() {
31
33
  const entry = store.fetchHistory.value.find((entry2) => entry2.model === model && entry2.method === method && entry2.query === JSON.stringify(getQuery()));
32
34
  if (entry?.ssr && nuxtApp.isHydrating)
33
35
  return false;
@@ -35,12 +37,20 @@ export async function useZenstackReadMany(model, opts = {}) {
35
37
  return false;
36
38
  return true;
37
39
  }
40
+ function shouldFetchInitially() {
41
+ if (opts.immediate === false)
42
+ return false;
43
+ return canFetchAutomatically();
44
+ }
38
45
  function updateData() {
39
- data.value = store.getMany(model);
46
+ const items = store.getMany(model);
47
+ const filteredItems = items.filter((el) => matchesWhere(el, toValue(opts.where)));
48
+ const orderedFilteredItems = sort(filteredItems, toValue(opts.orderBy));
49
+ data.value = orderedFilteredItems;
40
50
  }
41
51
  function getQuery() {
42
52
  const json = stringify({
43
- include: toValue(opts.include),
53
+ include: opts.include,
44
54
  where: toValue(opts.where),
45
55
  orderBy: toValue(opts.orderBy),
46
56
  skip: toValue(opts.skip),
@@ -61,15 +71,18 @@ export async function useZenstackReadMany(model, opts = {}) {
61
71
  });
62
72
  error.value = null;
63
73
  status.value = "pending";
74
+ canFetchMore.value = false;
64
75
  try {
65
76
  const res = await _fetch(url, { method, query });
66
77
  store.setMany(model, res.data);
67
78
  updateData();
68
79
  status.value = "success";
80
+ canFetchMore.value = res.data.length === toValue(opts.take);
69
81
  } catch (err) {
70
82
  error.value = err.data;
71
83
  status.value = "error";
84
+ canFetchMore.value = true;
72
85
  }
73
86
  }
74
- return { data, error, status, refetch };
87
+ return { data, error, status, canFetchMore, refetch };
75
88
  }
@@ -0,0 +1,5 @@
1
+ import type { $Zitem, $ZorderBy, $Zinclude, $Zmodel } from '#build/types/nuxt-zenstack';
2
+ /**
3
+ * @note Sort by relational fields not supported
4
+ */
5
+ export declare function sort<Zmodel extends $Zmodel, Zinclude extends $Zinclude<Zmodel>, Zitem extends $Zitem<Zmodel, Zinclude>>(items: Zitem[], orderBy: $ZorderBy<Zmodel>): Zitem[];
@@ -0,0 +1,25 @@
1
+ export function sort(items, orderBy) {
2
+ if (!items.length || !orderBy)
3
+ return items;
4
+ return items.sort((a, b) => {
5
+ const orderByArray = Array.isArray(orderBy) ? orderBy : [orderBy];
6
+ for (const orderByObj of orderByArray) {
7
+ const orderByEntries = Object.entries(orderByObj);
8
+ for (const [field, direction] of orderByEntries) {
9
+ if (direction !== "asc" && direction !== "desc") continue;
10
+ const aValue = a[field];
11
+ const bValue = b[field];
12
+ if (aValue == null && bValue == null) continue;
13
+ if (aValue == null) return 1;
14
+ if (bValue == null) return -1;
15
+ let comparison = 0;
16
+ if (aValue < bValue) comparison = -1;
17
+ else if (aValue > bValue) comparison = 1;
18
+ if (comparison !== 0) {
19
+ return direction === "asc" ? comparison : -comparison;
20
+ }
21
+ }
22
+ }
23
+ return 0;
24
+ });
25
+ }
@@ -0,0 +1,2 @@
1
+ import type { $Zitem, $Zwhere, $Zinclude, $Zmodel } from '#build/types/nuxt-zenstack';
2
+ export declare function matchesWhere<Zmodel extends $Zmodel, Zinclude extends $Zinclude<Zmodel>>(item: $Zitem<Zmodel, Zinclude>, where: $Zwhere<Zmodel>): boolean;
@@ -0,0 +1,235 @@
1
+ export function matchesWhere(item, where) {
2
+ if (!where) return true;
3
+ if (!item) return false;
4
+ const itemRecord = item;
5
+ const whereRecord = where;
6
+ for (const key of Object.keys(whereRecord)) {
7
+ if (whereRecord[key] === void 0) continue;
8
+ const filter = whereRecord[key];
9
+ if (key === "AND") {
10
+ if (Array.isArray(filter)) {
11
+ if (!filter.every((cond) => matchesWhere(item, cond))) return false;
12
+ } else {
13
+ if (!matchesWhere(item, filter)) return false;
14
+ }
15
+ continue;
16
+ }
17
+ if (key === "OR") {
18
+ if (Array.isArray(filter)) {
19
+ if (!filter.some((cond) => matchesWhere(item, cond))) return false;
20
+ } else {
21
+ if (!matchesWhere(item, filter)) return false;
22
+ }
23
+ continue;
24
+ }
25
+ if (key === "NOT") {
26
+ if (Array.isArray(filter)) {
27
+ if (filter.some((cond) => matchesWhere(item, cond))) return false;
28
+ } else {
29
+ if (matchesWhere(item, filter)) return false;
30
+ }
31
+ continue;
32
+ }
33
+ const value = itemRecord[key];
34
+ if (!checkCondition(value, filter)) {
35
+ return false;
36
+ }
37
+ }
38
+ return true;
39
+ }
40
+ function checkCondition(value, filter) {
41
+ if (filter === null) {
42
+ return value === null;
43
+ }
44
+ if (filter === void 0) {
45
+ return true;
46
+ }
47
+ if (typeof filter !== "object" && typeof filter !== "function") {
48
+ if (value instanceof Date && typeof filter === "string") {
49
+ return value.toISOString() === filter;
50
+ }
51
+ return value === filter;
52
+ }
53
+ if (value instanceof Date && filter instanceof Date) {
54
+ return value.getTime() === filter.getTime();
55
+ }
56
+ const filterRecord = filter;
57
+ const keys = Object.keys(filterRecord);
58
+ if (keys.length === 0) return true;
59
+ let caseInsensitive = false;
60
+ if (filterRecord.mode === "insensitive") {
61
+ caseInsensitive = true;
62
+ }
63
+ for (const op of keys) {
64
+ if (op === "mode") continue;
65
+ let expected = filterRecord[op];
66
+ let actual = value;
67
+ if (["lt", "lte", "gt", "gte", "equals", "not"].includes(op)) {
68
+ if (actual instanceof Date && typeof expected === "string") {
69
+ const d = new Date(expected);
70
+ if (!Number.isNaN(d.getTime())) expected = d;
71
+ }
72
+ }
73
+ if (caseInsensitive && typeof actual === "string" && typeof expected === "string") {
74
+ if (["equals", "not", "contains", "startsWith", "endsWith"].includes(op)) {
75
+ actual = actual.toLowerCase();
76
+ expected = expected.toLowerCase();
77
+ }
78
+ }
79
+ switch (op) {
80
+ case "equals":
81
+ if (actual instanceof Date && expected instanceof Date) {
82
+ if (actual.getTime() !== expected.getTime()) return false;
83
+ } else if (actual !== expected) {
84
+ return false;
85
+ }
86
+ break;
87
+ case "not":
88
+ if (typeof expected === "object" && expected !== null && !Array.isArray(expected) && !(expected instanceof Date)) {
89
+ if (checkCondition(value, expected)) return false;
90
+ } else {
91
+ if (actual instanceof Date && expected instanceof Date) {
92
+ if (actual.getTime() === expected.getTime()) return false;
93
+ } else if (actual === expected) {
94
+ return false;
95
+ }
96
+ }
97
+ break;
98
+ case "in":
99
+ if (!Array.isArray(expected)) return false;
100
+ if (caseInsensitive && typeof actual === "string") {
101
+ const lowerVal = actual.toLowerCase();
102
+ const lowerList = expected.map((e) => typeof e === "string" ? e.toLowerCase() : e);
103
+ if (!lowerList.includes(lowerVal)) return false;
104
+ } else {
105
+ if (actual instanceof Date) {
106
+ const time = actual.getTime();
107
+ if (!expected.some((e) => e instanceof Date && e.getTime() === time || e === actual)) return false;
108
+ } else {
109
+ if (!expected.includes(actual)) return false;
110
+ }
111
+ }
112
+ break;
113
+ case "notIn":
114
+ if (!Array.isArray(expected)) return false;
115
+ if (caseInsensitive && typeof actual === "string") {
116
+ const lowerVal = actual.toLowerCase();
117
+ const lowerList = expected.map((e) => typeof e === "string" ? e.toLowerCase() : e);
118
+ if (lowerList.includes(lowerVal)) return false;
119
+ } else {
120
+ if (actual instanceof Date) {
121
+ const time = actual.getTime();
122
+ if (expected.some((e) => e instanceof Date && e.getTime() === time || e === actual)) return false;
123
+ } else {
124
+ if (expected.includes(actual)) return false;
125
+ }
126
+ }
127
+ break;
128
+ case "lt":
129
+ if (typeof actual === "number" && typeof expected === "number") {
130
+ if (!(actual < expected)) return false;
131
+ } else if (actual instanceof Date && expected instanceof Date) {
132
+ if (!(actual < expected)) return false;
133
+ } else if (typeof actual === "string" && typeof expected === "string") {
134
+ if (!(actual < expected)) return false;
135
+ } else {
136
+ if (!(actual < expected)) return false;
137
+ }
138
+ break;
139
+ case "lte":
140
+ if (!(actual <= expected)) return false;
141
+ break;
142
+ case "gt":
143
+ if (!(actual > expected)) return false;
144
+ break;
145
+ case "gte":
146
+ if (!(actual >= expected)) return false;
147
+ break;
148
+ case "contains":
149
+ if (typeof actual !== "string" || typeof expected !== "string" || !actual.includes(expected)) return false;
150
+ break;
151
+ case "startsWith":
152
+ if (typeof actual !== "string" || typeof expected !== "string" || !actual.startsWith(expected)) return false;
153
+ break;
154
+ case "endsWith":
155
+ if (typeof actual !== "string" || typeof expected !== "string" || !actual.endsWith(expected)) return false;
156
+ break;
157
+ // Relations
158
+ case "some":
159
+ if (!Array.isArray(value)) return false;
160
+ if (!value.some((v) => checkInternal(v, expected))) return false;
161
+ break;
162
+ case "every":
163
+ if (!Array.isArray(value)) return false;
164
+ if (!value.every((v) => checkInternal(v, expected))) return false;
165
+ break;
166
+ case "none":
167
+ if (!Array.isArray(value)) return false;
168
+ if (value.some((v) => checkInternal(v, expected))) return false;
169
+ break;
170
+ case "is":
171
+ if (expected === null) {
172
+ if (value != null) return false;
173
+ } else {
174
+ if (value == null) return false;
175
+ if (!checkInternal(value, expected)) return false;
176
+ }
177
+ break;
178
+ case "isNot":
179
+ if (expected === null) {
180
+ if (value == null) return false;
181
+ } else {
182
+ if (value == null) break;
183
+ if (checkInternal(value, expected)) return false;
184
+ }
185
+ break;
186
+ default:
187
+ if (value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date)) {
188
+ if (!checkCondition(value[op], expected)) return false;
189
+ } else {
190
+ return false;
191
+ }
192
+ }
193
+ }
194
+ return true;
195
+ }
196
+ function checkInternal(item, where) {
197
+ return matchesWhereInternal(item, where);
198
+ }
199
+ function matchesWhereInternal(item, where) {
200
+ if (!where) return true;
201
+ if (!item) return false;
202
+ for (const key of Object.keys(where)) {
203
+ if (where[key] === void 0) continue;
204
+ const filter = where[key];
205
+ if (key === "AND") {
206
+ if (Array.isArray(filter)) {
207
+ if (!filter.every((cond) => matchesWhereInternal(item, cond))) return false;
208
+ } else {
209
+ if (!matchesWhereInternal(item, filter)) return false;
210
+ }
211
+ continue;
212
+ }
213
+ if (key === "OR") {
214
+ if (Array.isArray(filter)) {
215
+ if (!filter.some((cond) => matchesWhereInternal(item, cond))) return false;
216
+ } else {
217
+ if (!matchesWhereInternal(item, filter)) return false;
218
+ }
219
+ continue;
220
+ }
221
+ if (key === "NOT") {
222
+ if (Array.isArray(filter)) {
223
+ if (filter.some((cond) => matchesWhereInternal(item, cond))) return false;
224
+ } else {
225
+ if (matchesWhereInternal(item, filter)) return false;
226
+ }
227
+ continue;
228
+ }
229
+ const value = item[key];
230
+ if (!checkCondition(value, filter)) {
231
+ return false;
232
+ }
233
+ }
234
+ return true;
235
+ }
@@ -0,0 +1,6 @@
1
+ import type { $Zmodel } from '#build/types/nuxt-zenstack';
2
+ export declare function useZenstackRealtime(): {
3
+ subscribe: (models: $Zmodel[]) => void;
4
+ unsubscribe: (models: $Zmodel[]) => void;
5
+ getSubscriptions: () => (string | number | symbol)[];
6
+ };